// Show the visual stuff with men in a unit turning to face the target and shooting.
FUNCTION TurnMenAndShoot(me, x, y, unit, volley)
{
	int facing;
	int originalFacing;
	int i;
	int shooterX;
	int shooterY;
	int dx;
	int dy;
	int distance;
	int projectileIndex;
	
	distance = GetDistanceBetween(me, unit);

	// Wait on unit moving before displaying effects
	AddVizWaitOnMove(me);
	AddVizWaitOnTurnAll(me);

	// focus camera on unit and play fire effect
	AddVizCamUnit(me);

	originalFacing = GetUnitFacing(me);
	
	// Turn men to face target
	facing = GetFacingForShooting(me, unit, x, y);
	if (facing != originalFacing)
		{
			ChangeMenFacing(me, facing);
		}
		
  // Waiting for shooting animation to fully complete delays result appearing too much, but without any delay it appears too quick, 
  // so add in an arbitrary delay which is sufficent to allow the missiles to reach the target before the unit turns back or the results are displayed.
	AddVizAnimNoWait(me, "FIRE", 65535);
	

	if (IsArtillery(me) == 1)
		{
			AddVizDelay(38 + (distance * 3)); // may need tweaking
		}
	else
		{
			AddVizDelay(28 + (distance * 5)); // may need tweaking
		}


  AddVizFunctionCall("PlayImpactSounds", me, unit);
	
	// Turn men back to face direction unit is facing
	if (facing != originalFacing)
		{
			AddVizFunctionCall("ChangeMenFacing", me, originalFacing);
			AddVizFunctionCall("ResetTemporaryFacingGlobal", GetUnitSide(me));
		}

	// If  the shooters were artillery or ship , and hence perhaps far away, then move the camera to look at the target
	if ((IsArtillery(me) == 1) || (IsUnitSquadType(me, "Ship") == 1))
		{
			AddVizCamUnit(unit);
		}
}

// Determine facing for individual men to face shooting target - within 5 degrees currently
FUNCTION GetFacingForShooting(me, unit, tilex, tiley)
{
	int original_facing;
	int facing;
  int angle;
  int best_angle;
  int best_facing;
  
  best_angle = 180;

	original_facing = GetUnitFacing(me);

	// Get facing that would be closest to directly facing the target square
	for (facing = 0; facing < 360; facing += 5)
	{
		SetUnitFacing(me, facing, 1);
		angle = GetAngleFromTile(tilex, tiley, me);
		if (angle < best_angle)
			{
				best_facing = facing;
				best_angle = angle;
			}
	}

	SetUnitFacing(me, original_facing, 1);
	
	return best_facing;
}

// Change facing of men in unit without changing facing of unit (and hence their positions on the ground). Used in TurnMenAndShoot() to turn shooters to face target unit.
FUNCTION ChangeMenFacing(me, facing)
{
	int relativeFacing;
	int unitFacing;
	int i;
	
	unitFacing = GetUnitFacing(me);
	
	relativeFacing = facing - unitFacing;
	
	// Small angles treated as zero to ensure that it goes back to zero when archers turn back - as for some reason the original facing passed to the function and unitFacing are not always equal.
	if (Abs(relativeFacing) < 5)
		{
			relativeFacing = 0;
		}
	
	if (relativeFacing < 0)
		{
			relativeFacing += 360;
		}
		
	//DebugLogX("unit, relativeFacing", me, relativeFacing, 999,999);
		
	gTemporaryRelativeFacing[GetUnitSide(me)] = relativeFacing; // Pass new relative facing of men to FORMATION_CALLBACK in global variable.
		
	AddVizUnitFormation(me);
}

// It is vital that gTemporaryFacingGlobal[side] is reset to 0 after archers turn back, as this global is shared by all units on same side.
// This function is called to make absolutely sure.
FUNCTION ResetTemporaryFacingGlobal(side)
{
	gTemporaryRelativeFacing[side] = 0;
}

// Calculated shooting attack, but don't remove casualties yet
FUNCTION Shooting_Attack(unit, damage)
{
	int lossFactor;
	int moraleDamage;
	int current_strength;
	int men_lost;
	int loss_factor;

	if ((unit != -1) && (GetUnitDead(unit) == 0))
	{
		current_strength = GetAttrib(unit, "TotalMen");
		men_lost = GetLosses(unit, damage);
		lossFactor = GetLossFactor(unit, damage); // Avoid 0 casualties on elephants reducing lossFactor to 0.
//		lossFactor = men_lost * 10000;
//		lossFactor /= current_strength;

		moraleDamage = ConvertLossFactorToMoraleDamage(unit, lossFactor); // Remove part of moraleDamage diluting effect of very large units
	}

	return moraleDamage ;
}


FUNCTION CalculateCoverModifier(me, unit, x, y, ignoreMovement)
{
int coverModifier ;
int facing ;
int obstacleType;
int forts;

	coverModifier = 100 ; // default

	if (ignoreMovement == 1)
		{
			coverModifier = 0 ; // flag to use cover
		}

	// get a cover bonus if we are sneaking or we haven't moved normally.
	if( GetAttrib (unit, "SneakMode") == 1 )
		{
			coverModifier = 0 ; // flag to use cover
		}

	// did we move in normal mode?  If not or moved in sneak then use cover
	if( GetAttrib (unit, "moved") != 1 )
		{
			coverModifier = 0 ; // flag to use cover
		}

	// so if we are moving across ground and not sneaking we use the higher damage amount
	if (coverModifier == 100)
		{
			// use the higher % of damage
			coverModifier = GetTerrainCoverValue(x, y, 1) ;
		}
	else
		{
			// me == -1 if it is off map bombardment. In this case give full cover bonus or if you are facing the enemy
			if ( me == -1)
				{
					facing = 0 ;
				}
			else
				{
					facing = GetAngleFrom(me, unit);
				}

			// if facing enemy you get full effect of cover
			if (facing <= 60 )
				{
					coverModifier = GetTerrainCoverValue(x, y, 0);
				}
			// if side on or behind, cover bonus for stationary units is reduced by 50% (by getting a value midway betwen stationary and moving cover benefits)
			else
				{
					coverModifier = GetTerrainCoverValue(x, y, 1) - GetTerrainCoverValue(x, y, 0) ;
					coverModifier /= 2 ;
					coverModifier += GetTerrainCoverValue(x, y, 0) ;
				}
		}

	// Linear obstacles rated 1 or above improve cover rating for foot if not already in better or equal cover. Type 1 obstacles give no cover if the unit moved, fortifications do.
	if (IsFoot(unit) == 1)
		{
			obstacleType = IsTileEdgeDefendibleObstacle(GetUnitX(unit), GetUnitY(unit), GetUnitX(me), GetUnitY(me));

			if (obstacleType > 0)
				{
					if ((GetAttrib(unit, "moved") != 1) || (ignoreMovement == 1) || (obstacleType >= 2))
						{
							if ((IsArtillery(me) == 0) || (obstacleType >= 2))
								{
									forts = GetTerrainCoverValue(x, y, 3);
									if (forts > 1) // Medium or heavy fortifications
										{
											if (forts == 2) // Medium fortifications
												{
													coverModifier = Min(coverModifier, 34); // If these figures are changed, also need to be changed in HELPER_TERRAIN_TOOLTIP()
												}
											if (forts == 3) // Heavy fortifications
												{
													coverModifier = Min(coverModifier, 25); // If these figures are changed, also need to be changed in HELPER_TERRAIN_TOOLTIP()
												}
										}
									else
										{
											coverModifier = Min(coverModifier, 66); // If these figures are changed, also need to be changed in HELPER_TERRAIN_TOOLTIP()
										}
								}
						}
				}
		}

	return coverModifier ;

}


// check if the firing unit is in ambush and if it is give it a chance to reveal
FUNCTION AmbushCheck(me, unit)
{
	int x ;
	int y ;
	int ambushThreat ;
	int xx ;
	int yy ;

	//Log ("Ambush check", me, unit) ;

	// RBS Not sure what use we will make of this code, but we don't want artillery shooting to be treated as ambush. For now just prevent it working for artillery.
	// However, maybe we just want to use it for any troops in LOS Blocking Cover (including artillery)
	if (IsArtillery(me) == 0)
		{
			if (GetUnitActive(me) == 1)
				{
					if ( GetTileLOS(GetUnitX(me), GetUnitY(me), GetUnitSide(unit) ) != 1 )
						{
							x = GetUnitX (me) ;
							y = GetUnitY (me) ;

							// we are now visible until the start of our next turn
							if (GetAttrib (me, "AmbushSkill") <= Rand(0,100))
								{
									// only show spotted text when in cover
									if( GetTileLOS(GetUnitX(me), GetUnitY(me), GetUnitSide(unit) ) == 2)
										{
											AddVizUnitText(me, "IDS_UNIT_SPOTTED", "ffffff") ;
										}

									// show the tile.
									SetTileLOS(GetUnitX(me), GetUnitY(me), GetUnitSide(unit), 1) ;
									SetForceVisible(me, 1) ;
									SetUnitForReaction(me) ;

									// set AI ambush map to max value
									ambushThreat = 100 ;
								}
							else
								{
									xx = x ;
									yy = y ;

									xx += Rand (-1,1) ;
									yy += Rand (-1,1) ;

									if( (x == xx) && (y==yy) )
										{
											xx++ ;
										}

									// if it is a player unit show ambush on the unit, otherwise randomise it to avoid giving it away

									// COMPLEX:  MP:  Need to keep the same actual entries in the viz queue, on the same tiles (in case it causes a delay for some other text)
									if (GetUnitSide (me) != GetShowSide() )
										{
											// always show
											AddVizText(x, y, "IDS_EMPTY_STRING", "ffffff", 1) ;
											// always show
											AddVizText(xx, yy, "IDS_UNIT_AMBUSH", "ffffff", 1) ;
										}
									else
										{
											// always show
											AddVizText(x, y, "IDS_UNIT_AMBUSH", "ffffff", 1) ;
											// always show
											AddVizText(xx, yy, "IDS_EMPTY_STRING", "ffffff", 1) ;
										}

									// increment ambush threat map
									ambushThreat = 30 ;
								}

							// only update if we are the player side, otherwise we are adding threat when we fire ourselves?
							if( GetUnitSide(unit) == GetAISide() )
								{
									AmbushThreatUpdate ( x, y, ambushThreat )
								}
						}
					else
						{
							// enemy units will react to me
							SetForceVisible(me, 1) ;
							SetUnitForReaction(me) ;
						}
				}
		}
}


// Increase the ambush threat when a unit fires from ambush
FUNCTION AmbushThreatUpdate (x, y, increase)
{
int i ;
int j ;
int distance ;
int value ;
int finalIncrease ;
int unit ;
int range ;

	// area of spread of threat
	range = 1 ;

	for (i=x-range ; i<= x+range; i++ )
	{
		for (j=y-range ; j<= y+range; j++)
		{
			if ( IsTileInMap (i, j) == 1 )
			{
				finalIncrease = 0 ;
				// if tile is visible we don't give it ambush threat
				if ( GetTileLOS(i, j, GetAISide() ) != 1 )
				{
					distance = GetDistanceBetweenTiles(x, y, i, j) ;
					distance += 1 ;

					value = GetThreatMapValue(i, j, 2) ;
					finalIncrease = increase / distance ;
					finalIncrease += value ;

					if (finalIncrease > 100 )
					{
						finalIncrease = 100 ;
					}
					if (finalIncrease < 0)
					{
						finalIncrease = 0 ;
					}
				}
				SetThreatMapValue(i, j, 2, finalIncrease) ;
			}

		}
	}
}

// For missions with alarm level increase alarm
FUNCTION UpdateAlarmLevel (alarmIncrease)
{
int alarm ;

	alarm = GetUniversalVar("gCurrentAlarm") ;
	alarm += alarmIncrease ;
	SetUniversalVar("gCurrentAlarm", alarm) ;

}

// change view distance for LOS and cover LOS
FUNCTION ChangeViewDistance (me, type, change)
{
int los ;

	// type 0 is normal LOS
	if (type == 0 )
	{
		los = GetAttrib (me, "LOS") ;
		los+= change ;

		if (los < 15)
		{
			los = 15 ;
		}

		SetAttrib (me, "LOS", los) ;
	}

	// type 1 is CoverLOS
	if (type == 1 )
	{
		los = GetAttrib (me, "CoverLOS") ;
		los+= change ;

		if (los < 15)
		{
			// for units that have cover los never reduce to 0, e.g. infantry can always see one tile.
			if ( GetAttrib (me, "CoverLOS") > 0 )
			{
				los = 15 ;
			}
		}

		SetAttrib (me, "CoverLOS", los) ;
	}
}

// Removes losses and returns 100 times the % of current strength just lost. Any randomization of damage needs to be done before calling this function.
// Note that it returns 100 x % lost so as to minimise rounding errors.
// if combatLog = 1 then also writes to combat log
// Uses Work Array 0. Note that it calls GetCloseCombatFacingsAndRank() which uses Work Arrays 0 and 1, but this function does not use Work Array 0 till after that function has resolved.
FUNCTION RemoveCasualties(me, enemy, damage, combatLog, red)
{
	int current_strength;
	int men_lost;
	int lossFactor;
	int new_strength;
	int id;
	int impact;

	current_strength = GetAttrib(me, "TotalMen");

	men_lost = GetLosses(me, damage);

	DisplayCasualties(me, men_lost, combatLog, red);

//	lossFactor = men_lost * 10000;
//	lossFactor /= current_strength;
  lossFactor = GetLossFactor(me, damage); // Avoid 0 casualties on elephants reducing lossFactor to 0.

	Log ("Unit, Damage Factor, Men Lost, lossFactor (100 times % damage)", me, damage, men_lost, lossFactor);

	new_strength = current_strength - men_lost;

	//DebugLogX("unit, base man count, men_lost, killed", me, manCount, men_lost, killed);

	SetAttrib(me, "TotalMen", new_strength);
	
	impact = 0;	
	if (GetAttrib(me, "AnimSituation") == 1)
		{
			impact = 1;
		}
	if (enemy != -1)
		{
			if (GetAttrib(enemy, "AnimSituation") == 1)
				{
					impact = 1;
				}
		}

	if (impact == 1)
		{
			AddVizFunctionCall("CasualtiesDie", me); // Called in VizQ so that (hopefully) it won't get enacted before units move
		}
	else
		{
			CasualtiesDie(me);
		}

	SetUnitStatusFlag(me);

	// Also apply proportional losses to carried unit - currently none in FOG2. 
	id = GetLoadedUnit(me);
	if (id != -1)
		{
			current_strength = GetAttrib(id, "TotalMen");
			men_lost = GetLosses(id, damage);
			new_strength = current_strength - men_lost;
			SetAttrib(id, "TotalMen", new_strength);
			
			AddVizFunctionCall("CasualtiesDie", id); // Uncertain whether this code will have the correct result.
			
			SetUnitStatusFlag(id);
		}

	return lossFactor;
}

// Displays casualtees as floating text (and prints them to UI string). combatLog == 1 means also write to combat log
// If "StrengthMultipler" is less than 100%, gives random chance of rounding up reported casualties - to avoid invisible attrition.
FUNCTION DisplayCasualties(me, casualties, combatLog, red)
	{
		casualties *= GetStrengthMultiplier();
		if (GetStrengthMultiplier() < 100)
			{
				casualties += Rand(0,99);
			}
		casualties /= 100;

		if (combatLog == 1)
			{
				PrintStringLiteralX(1, 0, "\n");
				PrintUnitPossession(1, 0, me, 0);
				PrintStringX(1, 0, "IDS_UI_COMBAT_RESULT_CASUALTIES");
				PrintStringLiteralX(1, 0, ": ");
				PrintIntX(1, 0, casualties);

				PrintStringLiteralX(2, 0, "\n");
				PrintUnitPossession(2, 0, me, 0);
				PrintStringX(2, 0, "IDS_UI_COMBAT_RESULT_CASUALTIES");
				PrintStringLiteralX(2, 0, ": ");
				PrintIntX(2, 0, casualties);
			}

		StartWorkString();
		PrintWorkStringLiteral("$");
		PrintWorkStringInt(casualties);
		if (red == 1)
			{
				AddVizUnitText(me, GetWorkString(), "ff0000", 1);
				if (UseTutorialMode() == 1)
					{
						if (gTutorialIndex < 2)
							{
								AddVizFunctionCall("ShowOnceOnlyTutorialString", 55, -1, 1, 0);
							}						
					}
			}
		else
			{
				AddVizUnitText(me, GetWorkString(), "ffd500", 1);
				if (UseTutorialMode() == 1)
					{
						if (gTutorialIndex < 2)
							{
								AddVizFunctionCall("ShowOnceOnlyTutorialString", 54, -1, 1, 0);
							}
					}
			}	
//    AddVizDelay(15); // May need to be tweaked // Probably not needed with animations
	}

// Reporting function to report % damage for tool tips.
FUNCTION PercentDamage(unit, damage)
{
int current_strength;
int original_strength;
int men_lost;
int resilience;
int percentLost;

	current_strength = GetAttrib(unit, "TotalMen");
	original_strength = StartingStrength(unit);
	resilience =  Max(GetAttrib(unit, "UnitSize"), 300);

// Men_lost is calculated from original strength of unit, as incoming fire is not reduced by unit's losses, and resilience is based on the full strength of the unit.
	men_lost = original_strength * damage;
	men_lost /= resilience;

	percentLost = men_lost * 100;
	percentLost /= current_strength;

	current_strength -= men_lost;

	return percentLost;
}

// returns combat damage reduction modifier (%) for Morale State and Disorder
// Attacking indicates whether unit is attacking
// enemy is unit we are deciding whther to attack (if any)
FUNCTION GetMoraleDisorderModifier(me, enemy, attacking)
{
int moraleState;
int disorderState;
int moraleModifier;
int disorderModifier;
int combinedModifier;

	moraleState = GetAttrib(me, "MoraleState");

	moraleModifier = 100 ;

	if (moraleState == 1)
	{
		moraleModifier = 74 ;
	}

	if (moraleState == 2)
	{
		moraleModifier = 55 ;
	}

	if (moraleState == 3)
	{
		moraleModifier = 0 ;
	}

	disorderState = PercentDisordered(me, enemy, attacking) * 10;
	disorderState /= 44; // This is an approximation to the above;

	disorderModifier = 100 - disorderState;

	combinedModifier = Min(moraleModifier, disorderModifier);

	return combinedModifier;
}


// Returns 0 if not disordered, otherwise % disordered: 100 = all Disordered, 200 = all Severely Disordered
// attacking indicates whether unit is attacking
// enemy is unit we are testing whether to attack (if any)
FUNCTION PercentDisordered(me, enemy, attacking)
{
	int worstDisorder;
	int disorder;
	int i;
	int facing;
	int id;
	int meX;
	int meY;
	int enemyX;
	int enemyY;
	int proceed;

	meX = GetUnitX(me);
	meY = GetUnitY(me);

	// Disorder from tile we are in
	worstDisorder = TileDisorder(me, meX, meY);

	// Disorder from tile troops we are already in close combat against are in - if we attacked them
	if (IsInCloseCombat(me) == 1)
		{
			if (GetAttrib(me, "Attacking") == 1)
				{
					// The only one we could have attacked is the first one in our unit's "Enemy" list.
					// (We don't want unit in open to be disordered if its attacking flag is already set (for attacking a unit in the open),
					// but it is being attacked by a new unit from terrain)
					// NOTE: This will be bugged if there is ever a situation where any "fossil" opponents get left in a unit's "Enemy" array - which should not be allowed to happen.
					id = GetAttribArray(me, "Enemy", 0);
					if (id != -1)
						{
							enemyX = GetUnitX(id);
							enemyY = GetUnitY(id);
							disorder = TileDisorder(me, enemyX, enemyY);
							worstDisorder = Max(disorder, worstDisorder);
						}
				}
		}

	if (attacking == 1)
		{
			// Also need to take into account enemy that we are checking whether to attack
			if (enemy != -1)
				{
					// Only proceed if not already in close combat with this enemy
					// - if you are then it will only cause disorder if it is the unit first attacked, as detected above
					proceed = 1;
					for (i = 0; i < 8; i++)
					{
						id = GetAttribArray(me, "Enemy", i);

						if (id == enemy)
							{
								i = 8;
								proceed = 0;
							}
					}

					if (proceed == 1)
						{
							enemyX = GetUnitX(enemy);
							enemyY = GetUnitY(enemy);

							disorder = TileDisorder(me, enemyX, enemyY);

							worstDisorder = Max(disorder, worstDisorder);
						}
				}
		}

  gElephantDisorder = 0; // this is purely for tutorial purposes
	// Mounted adjacent to elephants or camelry.
	if (IsMounted(me) == 1)
		{
			for (i = 0; i < 8; i++)
			{
				facing = GetFacingFromIndex(i);

				id = GetUnitOnTile(AdjacentX(meX, facing), AdjacentY(meY, facing));

				if (id != -1)
					{
						// Camelry don't scare other camelry
						if ((IsUnitSquadType(id, "Elephants") == 1) || ((IsUnitSquadType(id, "Camelry") == 1) && (IsUnitSquadType(me, "Camelry") == 0)))
							{
								// Enemy elephants and camels more scary than friendly ones
								if (GetUnitSide(id) != GetUnitSide(me))
									{
										disorder = 100;
									}
								else
									{
										disorder = 50;
									}
									
								gElephantDisorder = disorder;

								worstDisorder = Max(disorder, worstDisorder);
							}
					}
			}
		}

	return worstDisorder;
}

// Return tile disorder value for unit (Returns a standard value if unit not specified)
FUNCTION TileDisorder(me, x, y)
{
	int disorder;
	int terrain;

	disorder = 0;

	terrain = GetTerrainCoverValue(x, y, 4);

	// Medium or Heavy Fortifications
	if (GetTerrainCoverValue(x, y, 3) > 1)
		{
			if (me == -1)
				{
					terrain = 3;
				}
			else
				{
					// If inside fortification, count as clear
					if ((GetUnitX(me) == x) && (GetUnitY(me) == y))
						{
							terrain = 0;
						}
					else // If outside, count as difficult if across the fortifications
						{
							if (IsTileEdgeDefendibleObstacle(x, y, GetUnitX(me), GetUnitY(me)) > 1) // Fortified edge
								{
									terrain = 3;
								}
							else
								{
									terrain = 0;
								}
						}
				}
		}

	// Difficult
	if (terrain == 3)
		{
			if (me == -1)
				{
					disorder = 200;
				}
			else
				{
					if (IsUnitSquadType(me, "Light_Foot") == 1)
						{
							disorder = 0;
						}
					else
						{
							if (IsUnitSquadType(me, "Mixed_Foot") == 1)
								{
									disorder = 134; // Quick and dirty estimate based on 1/3 heavy, 2/3 bowmen. However, could be calculated according to proportion of weapon capabilities
								}
							else
								{
									if ((IsUnitSquadType(me, "Medium_Foot") == 1) || (IsUnitSquadType(me, "Bowmen") == 1) || (IsUnitSquadType(me, "Warriors") == 1) || (IsUnitSquadType(me, "Mob") == 1))
										{
											disorder = 100;
										}
									else
										{
											disorder = 200;
										}
								}
						}
				}
		}

	// Rough
	if (terrain == 2)
		{
			if (me == -1)
				{
					disorder = 100;
				}
			else
				{
					if ((IsUnitSquadType(me, "Light_Foot") == 1) || (IsUnitSquadType(me, "Medium_Foot") == 1) || (IsUnitSquadType(me, "Bowmen") == 1) || (IsUnitSquadType(me, "Warriors") == 1) || (IsUnitSquadType(me, "Mob") == 1))
						{
							disorder = 0;
						}
					else
						{
							if (IsUnitSquadType(me, "Mixed_Foot") == 1)
								{
									disorder = 34; // Quick and dirty estimate based on 1/3 pike, 2/3 shot. However, could be calculated according to proportion of weapon capabilities
								}
							else
								{
//                  if ((IsUnitSquadType(me, "Artillery") == 1) || ((IsUnitSquadType(me, "Knights") == 1) && (GetAttrib(me, "BodyArmour") > 200))) // May need to add Cataphracts if there are any in same period as Knights
									if ((IsUnitSquadType(me, "Artillery") == 1) || (IsUnitSquadType(me, "Cataphracts") == 1) || (IsUnitSquadType(me, "Heavy_Chariots") == 1) || (IsUnitSquadType(me, "Scythed_Chariots") == 1))
										{
											disorder = 200;
										}
									else
										{
											disorder = 100;
										}
								}
						}
				}
		}

	return disorder;
}



// Returns 1 if Tile is open terrain, 0 if not.
FUNCTION IsOpen(x,y)
{
	int ret;

	ret = 0;

	if (GetTerrainCoverValue(x, y, 4) == 0)
		{
			ret = 1;
		}

	return ret;
}

// Determines whether close combat is in open ground. Returns 1 if it is, 0 if it isn't.
FUNCTION IsCombatInOpen(me, enemy)
{
	int meX;
	int meY;
	int enemyX;
	int enemyY;
	int ret;

	ret = 0;

	meX = GetUnitX(me);
	meY = GetUnitY(me);
	enemyX = GetUnitX(enemy);
	enemyY = GetUnitY(enemy);

	if ((IsOpen(meX,meY) == 1) && (IsOpen(enemyX,enemyY) == 1))
		{
			ret = 1;
		}

	// Fighting across obstacle is not in open.
	if ((IsTileEdgeDefendibleObstacle(meX, meY, enemyX, enemyY) > -1) || (IsTileEdgeDefendibleObstacle(enemyX, enemyY, meX, meY) > -1))
		{
			ret = 0;
		}

	return ret;
}


// Determines if unit has enough pikes, heavy weapon men and swordsmen left to qualify as a keil. Returns 0 if not keil, otherwise 1.
// No individual man should have more than one of these attribs.
FUNCTION IsKeil(me)
{
	int percent;
	int original_block_size;
	int block_size;
	int ret;

	ret = 0;

	percent = GetAttrib(me, "Pike"); // Pikes

	if (percent >= 40)  // Unit at least 40% pikes
		{
			// Add heavy weapon men and swordsmen
			percent = percent + GetAttrib(me, "Heavy_Weapon") + GetAttrib(me,"Swordsmen");
			original_block_size = percent * GetBaseAttrib(me, "UnitSize"));
			original_block_size /= 100;
			if (original_block_size > 1200)
				{
					block_size = percent * GetAttrib(me, "UnitSize"));
					block_size *= GetAttrib(me, "TotalMen");
					block_size /= StartingStrength(me);
					block_size /= 100;

					if (block_size >= 800)
						{
							ret = 1;
						}
				}
		}

	return ret;
}

// Determines if unit has enough pikes, heavy weapon men and swordsmen left to qualify as 16 ranks deep pikes. Returns 0 if no 16 ranks deep pikes.
FUNCTION Percent16RanksPike(me)
{
	int percent;
	int block_size;
	int none_deep;
	int all_deep;
	int ret;
	int deep;
	int deep_bracket;

	ret = 0;

	percent = GetAttrib(me, "Pike"); // Pikes

	if (percent >= 40)  // Unit at least 40% pikes
		{
			// Add heavy weapon men and swordsmen - No individual man should have more than one of these attribs.
			percent = percent + GetAttrib(me, "Heavy_Weapon") + GetAttrib(me,"Swordsmen");
			all_deep = 1200 * percent;
			all_deep /= 100;
			none_deep = 900 * percent;
			none_deep /= 100;

			block_size = percent * GetBaseAttrib(me, "UnitSize");
			block_size *= GetAttrib(me, "TotalMen");
			block_size /= StartingStrength(me);
			block_size /= 100;

			if (block_size < all_deep)
				{
					deep_bracket = all_deep - none_deep;
					deep = block_size - none_deep;
					percent *= deep;
					percent /= deep_bracket;
				}

			ret = percent;
		}

	return ret;
}


// Determines if unit has enough pikes, heavy weapon men and swordsmen left to qualify as 12 ranks deep pikes. Returns 0 if no 12 ranks deep pikes.
FUNCTION Percent12RanksPike(me)
{
	int percent;
	int block_size;
	int none_deep;
	int all_deep;
	int ret;
	int deep;
	int deep_bracket;

	ret = 0;

	percent = GetAttrib(me, "Pike"); // Pikes

	if (percent >= 40)  // Unit at least 40% pikes
		{
			// Add heavy weapon men and swordsmen - No individual man should have more than one of these attribs.
			percent = percent + GetAttrib(me, "Heavy_Weapon") + GetAttrib(me,"Swordsmen");
			all_deep = 900 * percent;
			all_deep /= 100;
			none_deep = 600 * percent;
			none_deep /= 100;

			block_size = percent * GetBaseAttrib(me, "UnitSize");
			block_size *= GetAttrib(me, "TotalMen");
			block_size /= StartingStrength(me);
			block_size /= 100;

			if (block_size < all_deep)
				{
					deep_bracket = all_deep - none_deep;
					deep = block_size - none_deep;
					percent *= deep;
					percent /= deep_bracket;
				}

			ret = percent;
		}

	return ret;
}

// Determines if impact foot unit has enough men to qualify as deep impact foot. Returns 0 if no 12 ranks deep pikes.
FUNCTION Percent12RanksImpactFoot(me)
{
	int percent;
	int size;
	int none_deep;
	int all_deep;
	int ret;
	int deep;
	int deep_bracket;

	ret = 0;

	percent = GetAttrib(me, "Impact_Foot");

	if (percent >= 33)  // Unit at least 33% impact foot
		{
			percent = 100;
			all_deep = 900;
			none_deep = 600;

			size = GetBaseAttrib(me, "UnitSize");
			size *= GetAttrib(me, "TotalMen");
			size /= StartingStrength(me);

			if (size < all_deep)
				{
					deep_bracket = all_deep - none_deep;
					deep = size - none_deep;
					percent *= deep;
					percent /= deep_bracket;
				}

			ret = percent;
		}

	return ret;
}

// Returns 0 if unit is steady, otherwise the % of unit that it not steady.
// attacking indicates whether we are attacking
// enemy is the unit we are thinking of attacking (if any)
FUNCTION PercentNotSteady(me, enemy, attacking)
{
int ret;

ret = 0;

	if (GetAttrib(me, "MoraleState") > 0)
		{
			ret = 100;
		}
	else
		{
			ret = Min(PercentDisordered(me, enemy, attacking), 100);
		}

	return ret;
}

// Returns 1 if unit is foot in protective (vs cavalry) terrain, otherwise 0. Protection is lost if the unit attacks.
FUNCTION InProtectiveTerrain(me, enemy, attacking)
{
	int ret;

	ret = 0;

	if (IsFoot(me) == 1)
		{
			if (attacking == 0)
				{
					if (IsTileEdgeDefendibleObstacle(GetUnitX(me), GetUnitY(me), GetUnitX(enemy), GetUnitY(enemy)) > -1)
						{
							ret = 1;
						}
				}
		}

	return ret;
}


// Get effective combat size of unit (foot get maximum 600 UnitSize worth of men on any one enemy, mounted get 400).
FUNCTION GetCombatStrength(me)
{
	int strength;
	int maximum_effective_unit_size;

	if (IsMounted(me) == 1)
		{
			maximum_effective_unit_size = 40000;
		}
	else
		{
			maximum_effective_unit_size = 60000;
		}

	strength = GetAttrib(me, "TotalMen") * 100 ;
	strength /= StartingStrength(me);

	strength *= GetAttrib(me, "UnitSize");

	strength = Min(strength, maximum_effective_unit_size);
	strength /= 100;

	return strength;
}

// Returns 1 if unit is in close combat,otherwise 0
FUNCTION IsInCloseCombat(me)
{
	int i;
	int ret;
	int enemy;

	ret = 0;

	// Does not count as close combat if either side is routing
	if (GetAttrib(me, "MoraleState") < 3)
		{
			for (i = 0; i < 8; i++)
			{
				enemy = GetAttribArray(me, "Enemy", i);
				if  (enemy != -1)
				 {
					if (GetAttrib(enemy, "MoraleState") < 3)
						{
							ret = 1;
							i = 8;
						}
				 }
			}
		}

	return ret;
}

// Returns 1 if unit is in close combat vs enemy other than the one specified in the parameter,otherwise 0
FUNCTION IsInCloseCombatVsOtherEnemy(me,enemy)
{
	int i;
	int ret;
	int id;

	ret = 0;

	// Does not count as close combat if either side is routing
	if (GetAttrib(me, "MoraleState") < 3)
		{
			for (i = 0; i < 8; i++)
			{
				id = GetAttribArray(me, "Enemy", i);
				if ((id != -1) && (id != enemy))
				 {
					if (GetAttrib(id, "MoraleState") < 3)
						{
							ret = 1;
							i = 8;
						}
				 }
			}
		}

	return ret;
}

// Returns 1 if unit is to be displayed as in close combat,otherwise 0
FUNCTION DisplayIsInCloseCombat(me)
{
	int i;
	int ret;
	int enemy;

	ret = 0;

	// Does not count as close combat if either side is routing
	if (GetAttrib(me, "DisplayMoraleState") < 3)
		{
			for (i = 0; i < 8; i++)
			{
				enemy = GetAttribArray(me, "DisplayEnemy", i);
				if  (enemy != -1)
				 {
					if (GetAttrib(enemy, "DisplayMoraleState") < 3)
						{
							ret = 1;
							i = 8;
						}
				 }
			}
		}

	return ret;
}

// Returns 1 if unit can pursue,otherwise 0
FUNCTION MightPursue(me)
{
	int i;
	int ret;
	int enemy;
	int any_unrouted_enemy;

	ret = 0;
	any_unrouted_enemy = 0;

	// Count as pursuit if enemy in close combat "Enemy" attrib array are routing
	if (GetAttrib(me, "MoraleState") < 3)
		{
			for (i = 0; i < 8; i++)
			{
				enemy = GetAttribArray(me, "Enemy", i);
				if  (enemy != -1)
				 {
					if (GetAttrib(enemy, "MoraleState") < 3)
						{
							any_unrouted_enemy = 1;
						}
					else
						{
							ret = 1;
						}
				 }
			}
		}

	if (any_unrouted_enemy == 1)
		{
			ret = 0;
		}

	return ret;
}

// Returns -1 if not being pursued, otherwise id of first pursuer in list
FUNCTION IsBeingPursued(me)
{
	int i;
	int ret;
	int enemy;

	ret = -1;

	// Count as being pursued if we are routing and there are enemy units in our "Enemy" attrib array
	if (GetAttrib(me, "MoraleState") == 3)
		{
			for (i = 0; i < 8; i++)
			{
				enemy = GetAttribArray(me, "Enemy", i);
				if  (enemy != -1)
				 {
					if (GetAttrib(enemy, "MoraleState") < 3)
						{
						ret = enemy;
						i = 8;
						}
				 }
			}
		}

	return ret;
}


// Checks if a close combat/pursuit is already recorded in "Enemy" lists. If not, add it. Unit "me" should be the attacker.
// Main reason it is written this way is to add "Enemy" to each unit if charged fragged unit breaks prior to contact when charged and chargers still manage to end in contact.
// Aborts if the units are no longer adjacent by the time it is processed.
FUNCTION AddCloseCombat(me, enemy)
{
	int i;
	int already_recorded;

	// Abort if units no longer adjacent
	if (AreTilesAdjacent(GetUnitX(me),GetUnitY(me),GetUnitX(enemy),GetUnitY(enemy)) == 0)
		{
			return 0;
		}

	already_recorded = 0;

	SetAttrib(me, "Attacking", 1); // Note: assumes that me is always the attacker
	Log("Attacking", me);
	
	SetAttrib(me, "SteppedForward", 1);
	SetAttrib(enemy, "SteppedForward", 1);
	
	//DebugLogX("Add Close Combat: me, stepped forward, enemy, stepped forward", me, GetAttrib(me, "SteppedForward"), enemy, GetAttrib(enemy, "SteppedForward"));
	
	for (i = 0; i < 8; i++)
		{
			if (GetAttribArray(me,"Enemy",i) == enemy)
				{
					already_recorded = 1;
					i = 8;
				}
		}

	// If not already there, add conflict to both units's "Enemy" array.
	// Note that we should only need to check one unit, as if the conflict is in one unit's "Enemy" array, it should be in the other's too.
	if (already_recorded == 0)
		{
			for (i = 0; i < 8; i++)
			{
				if (GetAttribArray(me,"Enemy",i) == -1)
					{
						SetAttribArray(me,"Enemy",i, enemy);
						SetAttribArray(me,"DisplayEnemy",i, enemy);
						i = 8;
					}
			}

			for (i = 0; i < 8; i++)
			{
				if (GetAttribArray(enemy,"Enemy",i) == -1)
					{
						SetAttribArray(enemy,"Enemy",i, me);
						SetAttribArray(enemy,"DisplayEnemy",i, me);
						i = 8;
					}
			}
	 }
}


// Removes a close combat from both units' lists of active close combats
FUNCTION RemoveCloseCombat(me, enemy)
{
	int i;
	int id;

	for (i = 0; i < 8; i++)
	{
		if (GetAttribArray(me,"Enemy",i) == enemy)
			{
				SetAttribArray(me,"Enemy",i, -1);
				AddVizFunctionCall("SetAttribArray", me,"DisplayEnemy",i, -1)
				SetAttribArray(me,"MeleeDue",i, 0);
				i = 8;
			}
	}

	for (i = 0; i < 8; i++)
	{
		if (GetAttribArray(enemy,"Enemy",i) == me)
			{
				SetAttribArray(enemy,"Enemy",i, -1);
				AddVizFunctionCall("SetAttribArray",enemy,"DisplayEnemy",i, -1);
				SetAttribArray(enemy,"MeleeDue",i, 0);
				i = 8;
			}
	}

	// If unit is no longer in close combat with or pursuing any enemy, reset "Attacking" and "SteppedForward" flags and revert to WAIT animation
	if ((IsInCloseCombat(me) == 0) && (MightPursue(me) == 0))
		{
			SetAttrib(me, "Attacking", 0);
			Log("Remove Close Combat: Initiate WAIT anim", me);
			AddVizAnim(me, "WAIT", -65535);
			
			AddVizFunctionCall("EndStepForward", me);
		}

	if ((IsInCloseCombat(enemy) == 0) && (MightPursue(enemy) == 0))
		{
			SetAttrib(enemy, "Attacking", 0);
			Log("Remove Close Combat: Initiate WAIT anim", enemy);
			AddVizAnim(enemy, "WAIT", -65535);
			
			AddVizFunctionCall("EndStepForward", enemy);
		}
		
	// Reset AnimSituation Flag
	SetAttrib(me, "AnimSituation", 0);
	SetAttrib(enemy, "AnimSituation", 0);
}

// Removes all close combats from a unit's list of active close combats, and it from theirs
FUNCTION RemoveAllCloseCombats(me)
{
	int i;
	int id;

	for (i = 0; i < 8; i++)
		{
			id = GetAttribArray(me,"Enemy",i);

			if (id != -1)
				{
					RemoveCloseCombat(me, id);
				}
		}
}

// Turns unit to face whichever of its melee opponents is nearest to straight ahead.
FUNCTION TurnToFaceCloseCombat(me)
{
	int i;
	int angle;
	int new_angle;
	int opponent;
	int most_frontal_opponent;
	int targetX;
	int targetY;

	angle = 181;

	for (i = 0; i < 8; i++)
	{
		opponent = GetAttribArray(me, "Enemy", i);

		if (opponent != -1)
			{
				new_angle = GetAngleFrom(opponent,me);
				if (new_angle < angle)
					{
						angle = new_angle;
						most_frontal_opponent = opponent;
					}
			}
	}

	targetX = GetUnitX(most_frontal_opponent);
	targetY = GetUnitY(most_frontal_opponent);
	SetUnitFacing(me, GetFacing(GetUnitX(me),GetUnitY(me), targetX, targetY)); // Use this instead of AddVizTurnToFace() so that program knows this is new facing - otherwise break offs use wrong direction.
	AddVizUnitFormation(me);
}

// Returns the number of close combat opponents a unit has
FUNCTION NumberOfCloseCombatOpponents(me)
{
	int i;
	int ret;

	ret = 0;

	for (i = 0; i < 8; i++)
	{
		if (GetAttribArray(me, "Enemy", i) != -1)
		 {
			ret++
		 }
	}

	return ret;
}

// Returns 1 if unit is artillery, 0 if not
FUNCTION IsArtillery(me)
{
	int ret;

	ret = 0;
	
	if (me != -1) // Traps the case where the me parameter has been passed the id of a loaded unit that does not exist.
		{
			// Heavy/Medium or Light Artillery
			if ((IsUnitSquadType(me, "Artillery") ==  1) || (IsUnitSquadType(me, "Light_Artillery") ==  1))
				{
					ret = 1;
				}
		}

	return ret;
}

// Returns 1 if unit is limber, 0 if not
FUNCTION IsLimber(me)
{
	int ret;
	int unit;

	ret = 0;

	unit = GetLoadedUnit(me);
	if (unit != -1)
		{
			if (IsUnitSquadType(unit, "Artillery") ==  1)
				{
					ret = 1;
				}
		}

	unit = GetLoadedOn(me);
	if (unit != -1)
		{
			if (IsArtillery(unit) == 1)
				{
					ret = 1;
				}
		}

	return ret;
}

// Determines whether attack by me on enemy would be a Flank/Rear attack. Returns 0 if not flank attack, 1 if flank attack, 2 if rear attack.
// Note that this function does not take into account whether me is actually close enough to charge enemy, if only checks the relative positions.
// Also note that it only counts a rear threat if the enemy is directly behind.
// Note that pursuers who change target will take their initial position as being the tile adjacent to the new target,
// so effectively have much more lenient requirement for flank charging. This makes it all the more important for players to position their units taking
// into account possible pursuits, which is propably a good thing.
FUNCTION IsFlankRearAttack(me, enemy)
{
	int ret;
	int startX;
	int startY;

	ret = 0;

	startX = GetAttrib(me,"StartingPosX");
	startY = GetAttrib(me,"StartingPosY");

	// Battle Wagons and Troops in Square formation immune to effects of flank and rear attacks
//  if ((IsUnitSquadType(enemy, "Battle_Wagons") == 1) || (GetAttrib(enemy, "InSquare") == 1))
	if (GetAttrib(enemy, "InSquare") == 1)
		{
			ret = 0;
		}
	else
		{
			if (GetAngleFrom(me, enemy) >= 85) // Charger behind flank
				{
					if (GetAngleFromTile(startX, startY, enemy) >= 85) // Charger started turn behind flank
						{
							// Keils immune to ill effects of flank attack
							if (IsKeil(enemy) == 0)
								{
									ret = 1;
								}
						}
				}

			if (GetAngleFrom(me, enemy) > 138) // Charger behind rear
			// Unit directly diagonally behind flank (135 degrees approx) should NOT count as behind rear
				{
					if (GetAngleFromTile(startX, startY, enemy) > 138) // Charger started turn behind rear.
						{
							ret = 2;
						}
				}

			if (ret > 0)
				{
					// defensible obstacles and built-up-areas/buildings prevent flank/rear attack on foot. (No need to exclude Battle Wagons as they can't be flank/rear attacked anyway).
					if (IsFoot(enemy) == 1)
						{
							if ((IsTileEdgeDefendibleObstacle(GetUnitX(enemy), GetUnitY(enemy), GetUnitX(me), GetUnitY(me)) >= 0) || (GetTerrainCoverValue(GetUnitX(enemy), GetUnitY(enemy), 6) == 1))
								{
									ret = 0;
								}
						}
				}
		}

	return ret;
}

// Returns 1 if unit is light troops, 0 if not
FUNCTION IsLightTroops(me)
{
	int ret;

	ret = 0;

	// If unit is entirely of Light Foot or Light Horse
	if ((IsUnitSquadType(me, "Light_Foot") == 1) || (IsUnitSquadType(me, "Light_Horse") == 1))
		{
			ret = 1;
		}

	return ret;
}


// Returns 1 if unit qualifies as mounted, otherwise 0
FUNCTION IsMounted(me)
{
	int ret;

	ret = 0;

//  if ((IsUnitSquadType(me, "Knights") == 1) || (IsUnitSquadType(me, "Cataphracts") == 1) || (IsUnitSquadType(me, "Light_Chariots") == 1) || (IsUnitSquadType(me, "Heavy_Chariots") == 1) || (IsUnitSquadType(me, "Scythed_Chariots") == 1) || (IsUnitSquadType(me, "Light_Horse") == 1) || (IsUnitSquadType(me, "Cavalry") == 1) || (IsUnitSquadType(me, "Camelry") == 1))
	if ((IsUnitSquadType(me, "Cataphracts") == 1) || (IsUnitSquadType(me, "Light_Chariots") == 1) || (IsUnitSquadType(me, "Heavy_Chariots") == 1)  || (IsUnitSquadType(me, "Scythed_Chariots") == 1) || (IsUnitSquadType(me, "Light_Horse") == 1) || (IsUnitSquadType(me, "Cavalry") == 1) || (IsUnitSquadType(me, "Camelry") == 1))
		{
			ret = 1;
		}

	return ret;
}

// Returns 1 if unit qualifies as chariots, otherwise 0
FUNCTION IsChariots(me)
{
	int ret;

	ret = 0;

	if ((IsUnitSquadType(me, "Light_Chariots") == 1) || (IsUnitSquadType(me, "Heavy_Chariots") == 1) || (IsUnitSquadType(me, "Scythed_Chariots") == 1))
		{
			ret = 1;
		}

	return ret;
}

// Returns 1 if unit qualifies as foot, otherwise 0
FUNCTION IsFoot(me)
{
	int ret;

	ret = 0;

//  if ((IsUnitSquadType(me, "Determined_Foot") == 1) || (IsUnitSquadType(me, "Heavy_Foot") == 1) || (IsUnitSquadType(me, "Undrilled_Heavy_Foot") == 1) || (IsUnitSquadType(me, "Mixed_Foot") == 1) || (IsUnitSquadType(me, "Medium_Foot") == 1) || (IsUnitSquadType(me, "Bowmen") == 1) || (IsUnitSquadType(me, "Warriors") == 1) || (IsUnitSquadType(me, "Light_Foot") == 1) || (IsUnitSquadType(me, "Mob") == 1) || (IsUnitSquadType(me, "Battle_Wagons") == 1) || (IsUnitSquadType(me, "Artillery") == 1) || (IsUnitSquadType(me, "Light_Artillery") == 1))
	if ((IsUnitSquadType(me, "Heavy_Foot") == 1) || (IsUnitSquadType(me, "Undrilled_Heavy_Foot") == 1) || (IsUnitSquadType(me, "Mixed_Foot") == 1) || (IsUnitSquadType(me, "Medium_Foot") == 1) || (IsUnitSquadType(me, "Bowmen") == 1) || (IsUnitSquadType(me, "Warriors") == 1) || (IsUnitSquadType(me, "Light_Foot") == 1) || (IsUnitSquadType(me, "Mob") == 1) || (IsUnitSquadType(me, "Artillery") == 1) || (IsUnitSquadType(me, "Light_Artillery") == 1))
		{
			ret = 1;
		}

	return ret;
}

// Returns 1 if unit qualifies as shock troops, otherwise 0
FUNCTION IsShockTroops(me)
{
	int ret;

	ret = 0;

	if (IsLightTroops(me) == 0)
		{
			if ((GetAttrib(me, "Heavy_Lancers") > 25) || (GetAttrib(me, "Light_Lancers") > 25))
				{
					ret = 1;
				}
		}

	if ((IsUnitSquadType(me, "Heavy_Chariots") == 1) || (IsUnitSquadType(me, "Scythed_Chariots") == 1))
		{
			ret = 1;
		}

	if (Percent16RanksPike(me) > 0)
		{
			ret = 1;
		}

	if (GetAttrib(me, "Impact_Foot") > 25)
		{
			ret = 1;
		}

	if (GetAttrib(me, "Offensive_Spearmen") >= 50)
		{
			ret = 1;
		}

	return ret;
}

// Returns 1 if unit has threatened flank, otherwise 0
FUNCTION OldHasThreatenedFlank(me)
{
	int ret;

	ret = 0;

	if (EstimateFlankThreat(me, GetUnitFacing(me), 1) > 0)
		{
			ret = 1;
		}


	// Foot battle troops close to map border - if we decide to implement this
	if ((IsFoot(me) == 1) && (IsLightTroops(me) == 0))
		{
			if (ProximityToMapEdge(GetUnitX(me), GetUnitY(me)) != 0)
				{
					ret = 1;
				}
		}

	// Also needs to include check for infantry close to map edges and infantry with enemy cavalry not countered by friendly cavalry
	// FUNCTIONALITY TO BE ADDED

	return ret;
}

// This should only be used for malus, not for AI purposes. It is too slow to be used for shooting tooltips - so malus removed from shooting.
FUNCTION HasThreatenedFlank(me)
{
	int ret;
	int i;
	int x;
	int y;
	int side;
	int enemySide;
	int id;
	int distance;
	int meX;
	int meY;
	int canCharge;
	int total;
	int ap;
	int madeFreeTurn;
	int madeLargeTurn;
	int cannotControl;
	int shots;
	
	ret = 0;

	side = GetUnitSide(me);
	enemySide = GetEnemySide(me);
	meX = GetUnitX(me);
	meY = GetUnitY(me);

//  total = GetUnitCount(enemySide);
  total = MakeClosestUnitsToUnitList(me, 0);

	for (i = 0; i < total; i++)
	{
//		id = GetUnitID(enemySide, i) ;
    id = GetClosestUnit(i);
		x = GetUnitX(id) ;
		y = GetUnitY(id) ;

    if (IsUnitValid(id) == 1)
			{
				distance = GetDistanceFromUnit(me, x, y);
				
				if (distance <= MAX_MOVE_DISTANCE) 
					{
						if (distance <= GetBaseAttrib(id, "AP") / 4)
							{
								if (GetUnitLOSToUnit(me, id) == 1)
									{
										if (IsFlankRearAttack(id, me) > 0)
											{
												if ((IsLightTroops(id) == 0) || (IsLightTroops(me) == 1) || (IsArtillery(me) == 1)) // Light troops only cause cohesion drop if flank/rear charging light troops or artillery
													{
														// Save unit's current flags
														ap = GetAttrib(id, "AP");
														madeFreeTurn = GetAttrib(id, "MadeFreeTurn");
														madeLargeTurn = GetAttrib(id, "MadeLargeTurn");
														cannotControl = GetCannotControl(id);
														shots = GetAttrib(id, "Shots");
														
														// Set unit to next turn status
														SetAttrib(id, "AP", GetBaseAttrib(id, "AP"));
														SetAttrib(id, "MadeFreeTurn", 0);
														SetAttrib(id, "MadeLargeTurn", 0);
														SetCannotControl(id, 0);
														SetAttrib(id, "Shots", GetBaseAttrib(id, "Shots"));											
																					
														canCharge = CallUnitFunctionDirect(id, "CHECK_UNIT_ASSAULT", id, me);
														
//														Log("HasThreatenedFlank? unit, enemy, threatened?", me, id, canCharge);
														
														// Set unit back to current status
														SetAttrib(id, "AP", ap);
														SetAttrib(id, "MadeFreeTurn", madeFreeTurn);
														SetAttrib(id, "MadeLargeTurn", madeLargeTurn);
														SetCannotControl(id, cannotControl);
														SetAttrib(id, "Shots", shots);										
														
														if (canCharge >= 0)
															{
																ret = 1;
																i = total;
															}
													}
											}
									}
							}
					}
				else
					{
						i = total;
					}
			}
	}

	return ret;
}

// Returns the maximum shooting range of the unit
FUNCTION MaximumRange(me)
{
	int range;

	range = 0;

	if ((GetAttrib(me, "Bombs") > 0) || (GetAttrib(me, "Javelins") > 0) || (GetAttrib(me, "Handgun") > 0))
		{
			range = 1;
		}

	if (GetAttrib(me, "Sling") > 0)
		{
			range = 2;
		}

	if ((GetAttrib(me, "Bow") > 0) || (GetAttrib(me, "Crossbow") > 0))
		{
			if (IsMounted(me) == 1)
				{
					range = 2;
				}
			else
				{
					range = 4;
				}

			if ((GetAttrib(me, "Bow") <= 20) && (GetAttrib(me, "Crossbow") == 0)) // Stop Late Roman integral archers distant shooting -- mvp7 changed range to 2, default 0
				{
					range = 2;
				}
		}

	if (GetAttrib(me, "Light_Artillery") > 0)
		{
			range = 6;
		}

	if (GetAttrib(me, "Heavy_Artillery") > 0)
		{
			range = 9;
		}

		return range;
}

// Returns the range at which all shooters in unit can shoot at maximum effect
FUNCTION OptimumRange(me)
{
	int range;

	range = 0;

	if (GetAttrib(me, "Heavy_Artillery") > 0)
		{
			range = 9;
		}

	if (GetAttrib(me, "Light_Artillery") > 0)
		{
			range = 6;
		}

	if ((GetAttrib(me, "Sling") > 0) || (GetAttrib(me, "Bow") > 0) || (GetAttrib(me, "Crossbow") > 0))
		{
			range = 2;
		}

	if ((GetAttrib(me, "Bombs") > 0) || (GetAttrib(me, "Javelins") > 0))
		{
			range = 1;
		}

	return range;
}

// Calculates shooting weapon modifier for distance and POA. Also (with target set at -1) used to calculate unit's overall ShootingRating. Returns 0 if no weapons can shoot at this range.
// Note that it works on the assumption that no men can have more than one shooting weapon. A unit can have a mixture of shooting capabilities, but the total of all shooting capabilities must be less than or equal to 100%.
// Currently does not do Cover which it may be desirable to do separately
FUNCTION GetShootingWeaponModifier(me, target, distance, volley, test, print)
{
	int percent;
	int POA;
	int POA_adjustment;
	int total_modifier;
	int any_shooters;
	int range;
	int any_printed;
	int shots;
	int capability;
	int effective_armour;

	any_printed = 0;
	total_modifier = 0;
	any_shooters = 0;

	if (print == 1)
		{
			PrintStringIndexedX(1, test, "IDS_TT_SHOOTVOLLEY", volley);
		}

	// Handgun
	percent = GetAttrib(me, "Handgun");
	if (percent > 0)
		{
			any_shooters = 1;
			if (distance > 1)
				{
					percent = 0;
				}
			else
				{
					percent *= CloseRangeNonLightFootShootingModifier(me);
					percent /= 100;
				}

			if (print == 1)
				{
					PrintStringLiteralX(1, test, "\n");
					PrintStringIndexedX(1, test, "IDS_CAPABILITY", 4);
					PrintEffectiveShots(test, me, percent, volley, 0);
					any_printed = 1;
				}

			if (target == -1) // Used for calculating ShootingRating
				{
					POA = -50;  // Average POA vs Mounted and Foot
				}
			else
				{
					POA = 0;

//          if ((IsMounted(target) == 1) || (IsUnitSquadType(target, "Battle_Wagons") == 1)) // -1 POA for Handgun shooting at mounted troops or battle wagons
					if (IsMounted(target) == 1) // -1 POA for Handgun shooting at mounted troops
						{
							POA = -100;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 4);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_VS");
									PrintStringLiteralX(1, test, " ");
									if (IsMounted(target) == 1)
										{
											PrintStringX(1, test, "IDS_TT_MOUNTED_TROOPS");
										}
									else
										{
											PrintTroopTypesString(target);
										}
									PrintStringLiteralX(1, test, ": ");
									PrintShootingWeaponModifier(test, POA);
									any_printed = 1;
								}
						}

					if (IsUnitSquadType(target, "Elephants") == 1) // +1 POA vs Elephants
						{
							POA = 100;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 4);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_VS");
									PrintStringLiteralX(1, test, " ");
									PrintTroopTypesString(target);
									PrintStringLiteralX(1, test, ": ");
									PrintShootingWeaponModifier(test, POA);
									any_printed = 1;
								}
						}

//          if ((IsFoot(target) == 1) && (IsUnitSquadType(target, "Battle_Wagons") != 1)) // Target is foot other than battle_wagons, basic 0 POA
					if (IsFoot(target) == 1) // Target is foot, basic 0 POA
						{
							POA = 0;
						}
						
//					if (HasThreatenedFlank(me) == 1) // An extra -1 POA for Foot shooting while their own flank is threatened
//						{
//							POA -= 100;
//
//							if (print == 1)
//								{
//									PrintStringLiteralX(1, test, "\n");
//									PrintStringX(1, test, "IDS_TT_SHOOT_THREATENED_FLANK");
//									PrintStringLiteralX(1, test, ": ");
//									PrintShootingWeaponModifier(test, POA);
//									any_printed = 1;
//								}
//						}
				}

			total_modifier += AdjustedShootingPercent(percent, POA);
		}

	// Bow (Foot or Mounted)
	percent = GetAttrib(me, "Bow");
	//if (percent > 20) // Stop Late Romans long distance shooting -- mvp7 commented this and the brackets to disable the check
		//{
			any_shooters = 1;
			if (IsMounted(me) == 1 || percent <= 20) // mvp7 added OR for units with less than 20% bows
				{
					range = 2;
				}
			else
				{
					range = 4;
				}
			if (distance > range)
				{
					percent = 0;
				}
			else
				{
					if ((distance == 3) || (distance == 4))
						{
							percent /= 2;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 7);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_AT");
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_LONG_RANGE");
								}
						}
					else
						{
							percent *= CloseRangeNonLightFootShootingModifier(me);
							percent /= 100;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 7);
									if (IsMounted(me) != 1)
										{
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_TT_AT");
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_TT_SHORT_RANGE");
										}
								}
						}

					if (print == 1)
						{
							PrintEffectiveShots(test, me, percent, volley, 0);
							any_printed = 1;
						}
				}

			if (target == -1) // Used for calculating ShootingRating
				{
					POA = -55;  // Average POA vs Mounted and Foot
				}
			else
				{
					POA = -50; // Basic minus half a POA (in lieu of FOGR death roll adjustment)

					if (IsMounted(target) == 1)
						{
							if (IsChariots(target) == 0) // Chariots not rated for armour
								{
									POA += 50; // vs completely unarmoured cavalry

									effective_armour = GetAttrib(target, "BodyArmour");
									if (effective_armour < 100) // Make sub-Armoured armour more effective vs bows (Protected as good as Armoured. Shades between Unprotected and Protected double effective).
										{
											effective_armour *= 2;
											effective_armour = Min(effective_armour, 100);
										}
									POA_adjustment = effective_armour / 2;
									POA -= POA_adjustment;

									if (print == 1)
										{
											if (POA < 0)
												{
													PrintStringLiteralX(1, test, "\n");
													PrintStringIndexedX(1, test, "IDS_CAPABILITY", 7);
													PrintStringLiteralX(1, test, " ");
													PrintStringX(1, test, "IDS_TT_VS");
													PrintStringLiteralX(1, test, " ");
													PrintStringX(1, test, "IDS_TT_ARMOUR");
													PrintStringLiteralX(1, test, ": ");
													PrintShootingWeaponModifier(test, - POA_adjustment);
													any_printed = 1;
												}
										}
								}
						}
					else
						{
//              if (IsUnitSquadType(target, "Battle_Wagons") == 1) // -1 POA for Bow shooting at battle wagons
//                {
//                  POA -= 100;

//                  if (print == 1)
//                    {
//                      PrintStringLiteralX(1, test, "\n");
//                      PrintStringIndexedX(1, test, "IDS_CAPABILITY", 7);
//                      PrintStringLiteralX(1, test, " ");
//                      PrintStringX(1, test, "IDS_TT_VS");
//                      PrintStringLiteralX(1, test, " ");
//                      PrintTroopTypesString(target);
//                      PrintStringLiteralX(1, test, ": ");
//                      PrintShootingWeaponModifier(test, POA);
//                      any_printed = 1;
//                    }
//                }
//              else
//                {
//                  if ((IsFoot(target) == 1) && (IsUnitSquadType(target, "Battle_Wagons") != 1)) // Target is foot other than battle_wagons
									if (IsFoot(target) == 1) // Target is foot
										{
											// Note, the code below results in: BodyArmour 0 gives + 50 POA, 50 gives -25 POA, 100 gives -100 POA, BodyArmour 200 gives -150 POA, BodyArmour 300 gives -200 POA
											// (Not including the -50 for death roll adjustment)
											POA_adjustment = GetAttrib(target, "BodyArmour") - 100;
											POA_adjustment = POA_adjustment / 2;
											POA_adjustment = Max(POA_adjustment, -50);
											POA_adjustment += Min(GetAttrib(target, "BodyArmour"), 100);

											POA -= POA_adjustment;

											if (print == 1)
												{
													if (POA < 0)
														{
															PrintStringLiteralX(1, test, "\n");
															PrintStringIndexedX(1, test, "IDS_CAPABILITY", 7);
															PrintStringLiteralX(1, test, " ");
															PrintStringX(1, test, "IDS_TT_VS");
															PrintStringLiteralX(1, test, " ");
															PrintStringX(1, test, "IDS_TT_ARMOUR");
															PrintStringLiteralX(1, test, ": ");
															PrintShootingWeaponModifier(test, POA);
															any_printed = 1;
														}
												}
										}
									else
										{
											if (print == 1)
												{
													PrintStringLiteralX(1, test, "\n");
													PrintStringIndexedX(1, test, "IDS_CAPABILITY", 7);
													PrintStringLiteralX(1, test, " ");
													PrintStringX(1, test, "IDS_TT_VS");
													PrintStringLiteralX(1, test, " ");
													PrintTroopTypesString(target);
													PrintStringLiteralX(1, test, ": ");
													PrintShootingWeaponModifier(test, POA);
													any_printed = 1
												}
										}
										
//                }
						}
						
//					if (HasThreatenedFlank(me) == 1) // An extra -1 POA for shooting while own flank is threatened
//						{
//							POA -= 100;
//
//							if (print == 1)
//								{
//									PrintStringLiteralX(1, test, "\n");
//									PrintStringX(1, test, "IDS_TT_SHOOT_THREATENED_FLANK");
//									PrintStringLiteralX(1, test, ": ");
//									PrintShootingWeaponModifier(test, -100); // Specified directly otherwise reports cumulative POA
//									any_printed = 1;
//								}
//						}
				}

			//Log("Shooter, target, basic POA", me, target, POA);
			total_modifier += AdjustedShootingPercent(percent, POA);
		//}

	// Crossbow (Foot or Mounted)
	percent = GetAttrib(me, "Crossbow");
	if (percent > 0)
		{
			any_shooters = 1;
			if (IsMounted(me) == 1)
				{
					range = 2;
				}
			else
				{
					range = 4;
				}
			if (distance > range)
				{
					percent = 0;
				}
			else
				{
					if ((distance == 3) || (distance == 4))
						{
							percent /= 2;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 9);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_AT");
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_LONG_RANGE");
								}
						}
					else
						{
							percent *= CloseRangeNonLightFootShootingModifier(me);
							percent /= 100;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 9);
									if (IsMounted(me) != 1)
										{
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_TT_AT");
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_TT_SHORT_RANGE");
										}
								}
						}

					if (print == 1)
						{
							PrintEffectiveShots(test, me, percent, volley, 0);
							any_printed = 1;
						}
				}

			if (target == -1) // Used for calculating ShootingRating
				{
					POA = -100;  // Average POA vs Mounted and Foot
				}
			else
				{
					POA = -50; // Basic minus half a POA (in lieu of FOGR death roll adjustment)

					// Note: No further modification vs mounted troops. There might be if we follow FOGAM v2, in which case chariots will need to be excluded.

					if (IsFoot(target) == 1) // -1 POA for Crossbow shooting at foot (including battle wagons).
						{
							POA -= 100;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 9);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_VS");
									PrintStringLiteralX(1, test, " ");
//                  if (IsUnitSquadType(target, "Battle_Wagons") == 1)
//                    {
//                      PrintTroopTypesString(target);
//                    }
//                  else
//                    {
											PrintStringX(1, test, "IDS_TT_FOOT");
//                    }
									PrintStringLiteralX(1, test, ": ");
									PrintShootingWeaponModifier(test, POA);
									any_printed = 1;
								}
						}
					else
						{
							if (IsUnitSquadType(target, "Elephants") == 1) // +1 POA vs Elephants
								{
									POA += 100;

									if (print == 1)
										{
											PrintStringLiteralX(1, test, "\n");
											PrintStringIndexedX(1, test, "IDS_CAPABILITY", 9);
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_TT_VS");
											PrintStringLiteralX(1, test, " ");
											PrintTroopTypesString(target);
											PrintStringLiteralX(1, test, ": ");
											PrintShootingWeaponModifier(test, POA);
											any_printed = 1;
										}
								}
							else
								{
									if (print == 1)
										{
											PrintStringLiteralX(1, test, "\n");
											PrintStringIndexedX(1, test, "IDS_CAPABILITY", 9);
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_TT_VS");
											PrintStringLiteralX(1, test, " ");
											if (IsMounted(target) == 1)
												{
													PrintStringX(1, test, "IDS_TT_MOUNTED_TROOPS");
												}
											else
												{
													PrintTroopTypesString(target);
												}
											PrintStringLiteralX(1, test, ": ");
											PrintShootingWeaponModifier(test, POA);
											any_printed = 1;
										}
								}
						}
						
//					if (HasThreatenedFlank(me) == 1) // An extra -1 POA for shooting while own flank is threatened
//						{
//							POA -= 100;
//
//							if (print == 1)
//								{
//									PrintStringLiteralX(1, test, "\n");
//									PrintStringX(1, test, "IDS_TT_SHOOT_THREATENED_FLANK");
//									PrintStringLiteralX(1, test, ": ");
//									PrintShootingWeaponModifier(test, -100); // Specified directly otherwise reports cumulative POA
//									any_printed = 1;
//								}
//						}
				}

			//Log("Shooter, target, basic POA", me, target, POA);
			total_modifier += AdjustedShootingPercent(percent, POA);
		}

	// Javelins (Foot or Mounted)
	percent = GetAttrib(me, "Javelins");
	if (percent > 0)
		{
			any_shooters = 1;
			if (distance > 1)
				{
					percent = 0;
				}
			else
				{
					// Halve effect of non-light javelins (Aztecs etc.)
					if (IsLightTroops(me) == 0)
						{
							percent /= 2;
						}
				}

			if (print == 1)
				{
					PrintStringLiteralX(1, test, "\n");
					PrintStringIndexedX(1, test, "IDS_CAPABILITY", 13);
					PrintEffectiveShots(test, me, percent, volley, 0);
					any_printed = 1;
				}

			if (target == -1) // Used for calculating ShootingRating
				{
					POA = -55;  // Average POA vs Mounted and Foot
				}
			else
				{
					POA = -50; // Basic minus half a POA (in lieu of FOGR death roll adjustment)

					if (IsMounted(target) == 1)
						{
							if (IsChariots(target) == 0) // Chariots not rated for armour
								{
									POA += 50; // vs completely unarmoured cavalry

									effective_armour = GetAttrib(target, "BodyArmour");
									if (effective_armour < 100) // Make sub-Armoured armour more effective vs javelins (Protected as good as Armoured. Shades between Unprotected and Protected double effective).
										{
											effective_armour *= 2;
											effective_armour = Min(effective_armour, 100);
										}
									POA_adjustment = effective_armour / 2;
									POA -= POA_adjustment;

									if (print == 1)
										{
											if (POA < 0)
												{
													PrintStringLiteralX(1, test, "\n");
													PrintStringIndexedX(1, test, "IDS_CAPABILITY", 13);
													PrintStringLiteralX(1, test, " ");
													PrintStringX(1, test, "IDS_TT_VS");
													PrintStringLiteralX(1, test, " ");
													PrintStringX(1, test, "IDS_TT_ARMOUR");
													PrintStringLiteralX(1, test, ": ");
													PrintShootingWeaponModifier(test, - POA_adjustment);
													any_printed = 1;
												}
										}
								}
						}
					else
						{

//              if (IsUnitSquadType(target, "Battle_Wagons") == 1) // -1 POA for Bow shooting at battle wagons
//                {
//                  POA -= 100;

//                  if (print == 1)
//                    {
//                      PrintStringLiteralX(1, test, "\n");
//                      PrintStringIndexedX(1, test, "IDS_CAPABILITY", 13);
//                      PrintStringLiteralX(1, test, " ");
//                      PrintStringX(1, test, "IDS_TT_VS");
//                      PrintStringLiteralX(1, test, " ");
//                      PrintTroopTypesString(target);
//                      PrintStringLiteralX(1, test, ": ");
//                      PrintShootingWeaponModifier(test, POA);
//                      any_printed = 1;
//                    }
//                }
//              else
//                {
//                  if ((IsFoot(target) == 1) && (IsUnitSquadType(target, "Battle_Wagons") != 1)) // Target is foot other than battle_wagons
									if (IsFoot(target) == 1) // Target is foot
										{
											// Note, the code below results in: BodyArmour 0 gives + 50 POA, 50 gives -25 POA, 100 gives -100 POA, BodyArmour 200 gives -150 POA, BodyArmour 300 gives -200 POA
											// (Not including the -50 for death roll adjustment)
											POA_adjustment = GetAttrib(target, "BodyArmour") - 100;
											POA_adjustment = POA_adjustment / 2;
											POA_adjustment = Max(POA_adjustment, -50);
											POA_adjustment += Min(GetAttrib(target, "BodyArmour"), 100);

											POA -= POA_adjustment;

											if (print == 1)
												{
													if (POA < 0)
														{
															PrintStringLiteralX(1, test, "\n");
															PrintStringIndexedX(1, test, "IDS_CAPABILITY", 13);
															PrintStringLiteralX(1, test, " ");
															PrintStringX(1, test, "IDS_TT_VS");
															PrintStringLiteralX(1, test, " ");
															PrintStringX(1, test, "IDS_TT_ARMOUR");
															PrintStringLiteralX(1, test, ": ");
															PrintShootingWeaponModifier(test, POA);
															any_printed = 1;
														}
												}
										}
									else
										{
											if (IsUnitSquadType(target, "Elephants") == 1) // +1 POA vs Elephants
												{
													POA += 100;

													if (print == 1)
														{
															PrintStringLiteralX(1, test, "\n");
															PrintStringIndexedX(1, test, "IDS_CAPABILITY", 13);
															PrintStringLiteralX(1, test, " ");
															PrintStringX(1, test, "IDS_TT_VS");
															PrintStringLiteralX(1, test, " ");
															PrintTroopTypesString(target);
															PrintStringLiteralX(1, test, ": ");
															PrintShootingWeaponModifier(test, POA);
															any_printed = 1;
														}
												}
											else
												{
													if (print == 1)
														{
															PrintStringLiteralX(1, test, "\n");
															PrintStringIndexedX(1, test, "IDS_CAPABILITY", 13);
															PrintStringLiteralX(1, test, " ");
															PrintStringX(1, test, "IDS_TT_VS");
															PrintStringLiteralX(1, test, " ");
															PrintTroopTypesString(target);
															PrintStringLiteralX(1, test, ": ");
															PrintShootingWeaponModifier(test, POA);
															any_printed = 1
														}
												}
										}
//                }
						}
						
//					if (HasThreatenedFlank(me) == 1) // An extra -1 POA for shooting while own flank is threatened
//						{
//							POA -= 100;

//							if (print == 1)
//								{
//									PrintStringLiteralX(1, test, "\n");
//									PrintStringX(1, test, "IDS_TT_SHOOT_THREATENED_FLANK");
//									PrintStringLiteralX(1, test, ": ");
//									PrintShootingWeaponModifier(test, -100); // Specified directly otherwise reports cumulative POA
//									any_printed = 1;
//								}
//						}
				}

			//Log("Shooter, target, basic POA", me, target, POA);
			total_modifier += AdjustedShootingPercent(percent, POA);
		}

	// Sling (foot only)
	percent = GetAttrib(me, "Sling");
	if (percent > 0)
		{
			any_shooters = 1;
			if (distance > 2)
				{
					percent = 0;
				}
			else
				{
					// Halve effect of non-light slingers (if any)
					if (IsLightTroops(me) == 0)
						{
							percent /= 2;
						}
				}

			if (print == 1)
				{
					PrintStringLiteralX(1, test, "\n");
					PrintStringIndexedX(1, test, "IDS_CAPABILITY", 23);
					PrintEffectiveShots(test, me, percent, volley, 0);
					any_printed = 1;
				}

			if (target == -1) // Used for calculating ShootingRating
				{
					POA = -55;  // Average POA vs Mounted and Foot
				}
			else
				{
					POA = -50; // Basic minus half a POA (in lieu of FOGR death roll adjustment)

					if (IsMounted(target) == 1)
						{
							if (IsChariots(target) == 0) // Chariots not rated for armour
								{
									POA += 50; // vs completely unarmoured cavalry

									effective_armour = GetAttrib(target, "BodyArmour");
									if (effective_armour < 100) // Make sub-Armoured armour more effective vs sling (Protected as good as Armoured. Shades between Unprotected and Protected double effective).
										{
											effective_armour *= 2;
											effective_armour = Min(effective_armour, 100);
										}
									POA_adjustment = effective_armour / 2;
									POA -= POA_adjustment;

									if (print == 1)
										{
											if (POA < 0)
												{
													PrintStringLiteralX(1, test, "\n");
													PrintStringIndexedX(1, test, "IDS_CAPABILITY", 23);
													PrintStringLiteralX(1, test, " ");
													PrintStringX(1, test, "IDS_TT_VS");
													PrintStringLiteralX(1, test, " ");
													PrintStringX(1, test, "IDS_TT_ARMOUR");
													PrintStringLiteralX(1, test, ": ");
													PrintShootingWeaponModifier(test, - POA_adjustment);
													any_printed = 1;
												}
										}
								}
						}
					else
						{

//              if (IsUnitSquadType(target, "Battle_Wagons") == 1) // -1 POA for Bow shooting at battle wagons
//                {
//                  POA -= 100;

//                  if (print == 1)
//                    {
//                      PrintStringLiteralX(1, test, "\n");
//                      PrintStringIndexedX(1, test, "IDS_CAPABILITY", 23);
//                      PrintStringLiteralX(1, test, " ");
//                      PrintStringX(1, test, "IDS_TT_VS");
//                      PrintStringLiteralX(1, test, " ");
//                      PrintTroopTypesString(target);
//                      PrintStringLiteralX(1, test, ": ");
//                      PrintShootingWeaponModifier(test, POA);
//                      any_printed = 1;
//                    }
//                }
//              else
//                {
//                  if ((IsFoot(target) == 1) && (IsUnitSquadType(target, "Battle_Wagons") != 1)) // Target is foot other than battle_wagons
									if (IsFoot(target) == 1) // Target is foot
										{
											// Note, the code below results in: BodyArmour 0 gives + 50 POA, 50 gives -25 POA, 100 gives -100 POA, BodyArmour 200 gives -150 POA, BodyArmour 300 gives -200 POA
											// (Not including the -50 for death roll adjustment)
											POA_adjustment = GetAttrib(target, "BodyArmour") - 100;
											POA_adjustment = POA_adjustment / 2;
											POA_adjustment = Max(POA_adjustment, -50);
											POA_adjustment += Min(GetAttrib(target, "BodyArmour"), 100);

											POA -= POA_adjustment;

											if (print == 1)
												{
													if (POA < 0)
														{
															PrintStringLiteralX(1, test, "\n");
															PrintStringIndexedX(1, test, "IDS_CAPABILITY", 23);
															PrintStringLiteralX(1, test, " ");
															PrintStringX(1, test, "IDS_TT_VS");
															PrintStringLiteralX(1, test, " ");
															PrintStringX(1, test, "IDS_TT_ARMOUR");
															PrintStringLiteralX(1, test, ": ");
															PrintShootingWeaponModifier(test, POA);
															any_printed = 1;
														}
												}
										}
									else
										{
											if (print == 1)
												{
													PrintStringLiteralX(1, test, "\n");
													PrintStringIndexedX(1, test, "IDS_CAPABILITY", 23);
													PrintStringLiteralX(1, test, " ");
													PrintStringX(1, test, "IDS_TT_VS");
													PrintStringLiteralX(1, test, " ");
													PrintTroopTypesString(target);
													PrintStringLiteralX(1, test, ": ");
													PrintShootingWeaponModifier(test, POA);
													any_printed = 1
												}
										}
//                }
						}
						
//					if (HasThreatenedFlank(me) == 1) // An extra -1 POA for shooting while own flank is threatened
//						{
//							POA -= 100;
//
//							if (print == 1)
//								{
//									PrintStringLiteralX(1, test, "\n");
//									PrintStringX(1, test, "IDS_TT_SHOOT_THREATENED_FLANK");
//									PrintStringLiteralX(1, test, ": ");
//									PrintShootingWeaponModifier(test, -100); // Specified directly otherwise reports cumulative POA
//									any_printed = 1;
//								}
//						}
				}

			//Log("Shooter, target, basic POA", me, target, POA);
			total_modifier += AdjustedShootingPercent(percent, POA);
		}


	// Bombs
	percent = GetAttrib(me, "Bombs");
	if (percent > 0)
		{
			any_shooters = 1;

			if (distance > 1)
				{
					percent = 0;
				}

			if (print == 1)
				{
					PrintStringLiteralX(1, test, "\n");
					PrintStringIndexedX(1, test, "IDS_CAPABILITY", 6);
					PrintEffectiveShots(test, me, percent, volley, 0);
					any_printed = 1;
				}

			if (target == -1) // Used for calculating ShootingRating
				{
					POA = 0;  // Average POA vs Mounted and Foot
				}
			else
				{
					POA = 0;

					if (IsUnitSquadType(target, "Elephants") == 1) // +100 POA vs Elephants
						{
							POA = 100;
						}

					if (print == 1)
						{
							if (POA != 0)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 6);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_VS");
									PrintStringLiteralX(1, test, " ");
									if (IsMounted(target) == 1)
										{
											PrintStringX(1, test, "IDS_TT_MOUNTED_TROOPS");
										}
									else
										{
											if (IsFoot(target) == 1)
												{
													PrintStringX(1, test, "IDS_TT_FOOT");
												}
											else
												{
													PrintTroopTypesString(target);
												}
										}
									PrintStringLiteralX(1, test, ": ");
									PrintShootingWeaponModifier(test, POA);
									any_printed = 1;
								}
						}
						
//					if (HasThreatenedFlank(me) == 1) // An extra -1 POA for shooting while own flank is threatened
//						{
//							POA -= 100;

//							if (print == 1)
//								{
//									PrintStringLiteralX(1, test, "\n");
//									PrintStringX(1, test, "IDS_TT_SHOOT_THREATENED_FLANK");
//									PrintStringLiteralX(1, test, ": ");
//									PrintShootingWeaponModifier(test, -100); // Specified directly otherwise reports cumulative POA
//									any_printed = 1;
//								}
//						}
				}

			total_modifier += AdjustedShootingPercent(percent, POA);
		}

	// Artillery. Assumes that a unit cannot be a mix of different types of Artillery. Regimental guns are not treated as artillery.
	percent = GetAttrib(me, "Light_Artillery") + GetAttrib(me, "Heavy_Artillery");
	if (percent > 0)
		{
			if (print == 1)
				{
					any_printed = 1;
					PrintStringLiteralX(1, test, "\n");
					if (GetAttrib(me, "Light_Artillery") > 0)
						{
							capability = 1;
						}
					if (GetAttrib(me, "Heavy_Artillery") > 0)
						{
							capability = 3;
						}
					PrintStringIndexedX(1, test, "IDS_CAPABILITY", capability);
				}

			any_shooters = 1;
			if (distance > MaximumRange(me)) // Note that this will not work correctly if unit contains other shooters with longer range weapons
				{
					percent = 0;
				}
			else
				{
					if (distance > 6)
						{
							if (print == 1)
								{
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_AT");
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_LONG_RANGE");
								}

							// Long range artillery reduction - may need tweaking.
							percent *= 2;
							percent /= 3;
						}
					else
						{
							if ((print == 1) && (GetAttrib(me, "Light_Artillery") == 0))
								{
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_AT");
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_SHORT_RANGE");
								}
						}
				}


			if (print == 1)
				{
					PrintEffectiveShots(test, me, percent, volley, 1);
				}

			if (target == -1) // Used for calculating ShootingRating
				{
					POA = -50;  // Average POA vs Mounted and Foot
				}
			else
				{
					POA = -100; // -1 POA vs Foot

//          if ((IsMounted(target) == 1) || (IsUnitSquadType(target, "Battle_Wagons") == 1))  // target is mounted or battle wagons
					if (IsMounted(target) == 1)  // target is mounted or battle wagons
						{
							POA = 0;
						}

					if (IsUnitSquadType(target, "Elephants") == 1)
						{
							POA = 100;
						}

					if (IsArtillery(target) == 1)
						{
							POA = -200;
						}

					if (print == 1)
						{
							if (POA != 0)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", capability);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_VS");
									PrintStringLiteralX(1, test, " ");
									if (IsMounted(target) == 1)
										{
											PrintStringX(1, test, "IDS_TT_MOUNTED_TROOPS");
										}
									else
										{
											if (IsArtillery(target) == 1)
												{
													PrintStringX(1, test, "IDS_TT_ARTILLERY");
												}
											else
												{
													if (IsFoot(target) == 1)
														{
															PrintStringX(1, test, "IDS_TT_FOOT");
														}
													else
														{
															PrintTroopTypesString(target);
														}
												}
										}
									PrintStringLiteralX(1, test, ": ");
									PrintShootingWeaponModifier(test, POA);
									any_printed = 1;
								}
						}

					if ((GetAttrib(target,"UnitSize") > 750) || (TargetEnfiladed(me, target) == 1)) // large target or enfiladed. also need to add test for in square;
						{
							POA += 100;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", capability);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_VS");
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_TT_LARGE_INFILADED");
									PrintStringLiteralX(1, test, ": ");
									PrintShootingWeaponModifier(test, 100); // Specified directly otherwise reports cumulative POA
									any_printed = 1;
								}
						}
						
//					if (HasThreatenedFlank(me) == 1) // An extra -1 POA for shooting while own flank is threatened
//						{
//							POA -= 100;
//
//							if (print == 1)
//								{
//									PrintStringLiteralX(1, test, "\n");
//									PrintStringX(1, test, "IDS_TT_SHOOT_THREATENED_FLANK");
//									PrintStringLiteralX(1, test, ": ");
//									PrintShootingWeaponModifier(test, -100); // Specified directly otherwise reports cumulative POA
//									any_printed = 1;
//								}
//						}
				}
			total_modifier += AdjustedShootingPercent(percent, POA);
		}

	if (any_shooters > 0)
		{
			// Note that the adjustments below don't work identically to POAs as they occur after the other POAs have been taken into account

			// Adjust for quality
			POA = GetQuality(me) - 100; // Standard quality is 100. Only adjust POA for quality above or below 100.
			POA /= 2;  // 200 quality (+100 above average) is worth +50 POA
			total_modifier = AdjustedShootingPercent(total_modifier, POA);

			if (print == 1)
				{
					if (POA != 0)
						{
							PrintStringLiteralX(1, test, "\n");
							PrintStringX(1, test, "IDS_TT_QUALITY_MODIFIER");
							PrintStringLiteralX(1, test, ": ");
							PrintShootingWeaponModifier(test, POA);
							any_printed = 1;
						}
				}
				

			// Adjust for rain or snow - all except javelins
			if (GetAttrib(me, "Javelins") < 50) // This won't work correctly for units with less than 50% javelins and no other missile weapons, but no such units currently exist, nor are envisaged.
				{
					if ((GetWeather() == 1) || (GetWeather() == 2))
						{
							POA = -50;
							total_modifier = AdjustedShootingPercent(total_modifier, POA);
							
							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringX(1, test, "IDS_TT_WEATHER_MODIFIER");
									PrintStringLiteralX(1, test, ": ");
									PrintShootingWeaponModifier(test, POA);
									any_printed = 1;
								}							
						}
				}

			// Adjust for shooting at pursuers
			if (target != -1)
				{
					if (MightPursue(target) == 1)
						{
							total_modifier /= 2;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringX(1, test, "IDS_TT_SHOOTING_AT_PURSUERS");
									any_printed = 1;
								}
						}
				}

			// Adjust final modifier downwards when shooting at Light Foot or Light Horse, because they are in dispersed formation and harder to hit.
			// (Note that there in no such adjustment in FOGR, but FOGR light foot and light horse have twice as many "bases" as equivalent BA P&S units).
			if (target != -1)
				{
					if ((IsUnitSquadType(target, "Light_Foot") == 1) || (IsUnitSquadType(target, "Light_Horse") == 1))
						{
							total_modifier *= 66; // Amount of adjustment may need tweaking - maybe should be 50%
							total_modifier /= 100;

							if (print == 1)
								{
									PrintStringLiteralX(1, test, "\n");
									PrintStringX(1, test, "IDS_TT_SHOOTING_AT_SKIRMISHERS");
									any_printed = 1;
								}
						}
				}
				
			// Adjust final modifier for low ammunition
			if (IsAmmunitionLow(me) == 1)
				{
					total_modifier /= 2; // May need tweaking
					
					if (print == 1)
						{
							PrintStringLiteralX(1, test, "\n");
							PrintStringX(1, test, "IDS_TT_AMMUNITION_LOW");
							any_printed = 1;
						}	
				}

		}

	if (print == 1)
		{
			PrintStringLiteralX(1, test, "\n");
		}

	return total_modifier;
}


// Helper function for GetShootingWeaponModifier
FUNCTION AdjustedShootingPercent(percent, POA)
{
	int POAmodifier;

	POA = Min(POA, 200);
//  POA = Max(POA, -200);
	// Allow down to -250 POA to allow for blanket -50 POA for non gunpowder weapons. Still gives Raw troops shooting at Heavily Armoured foot a break.
	POA = Max(POA, -250);

	POAmodifier = 33 * POA;
	POAmodifier += 10000;
	percent *= POAmodifier;
	percent /= 10000;

	return percent;
}

// An overall rating for shooting power (will feed into threat maps). Longer range weapons exert more shooting power overall and hence threat. The resulting rating is only used for AI purposes, not to calculate results of combat.
// If enemy == -1 then it works out a general rating, if a unit id is specified it works out the shooting rating against that unit. Does not take into account losses, cohesion loss, disorder.
// If range == -1 calculate an average estimate of a variety of ranges, otherwise calculate the shooting rating at the specified range.
FUNCTION CalculateShootingRating(me,enemy,range)
{
	int ret;

	if (range != -1)
		{
			ret = GetShootingWeaponModifier(me, enemy, range, 3, 1, 0);
		}
	else
		{
			ret = GetShootingWeaponModifier(me, enemy, 1, 3, 1, 0) * 2;
			ret += GetShootingWeaponModifier(me, enemy, 2, 3, 1, 0);
			ret += GetShootingWeaponModifier(me, enemy, 4, 3, 1, 0);
			ret /= 4;
		}

	// Adjust for size of unit. (Maximum shooting strength = 6)
	ret *= Min(GetAttrib(me, "UnitSize"),600);
	ret /= 600;

	// Check for reduced shooting power for some mounted
	ret *= MountedShootingModifier(me);
	ret /= 100;

	return ret;
}

// Modified rating for shooting power. Takes into account losses, cohesion loss, disorder, enemy cover. The resulting rating is only used for AI purposes, not to calculate results of combat.
// If enemy == -1 then it works out a general rating, if a unit id is specified it works out the shooting rating against that unit.
// If range == -1 calculate an average estimate of a variety of ranges, otherwise calculate the shooting rating at the specified range.
FUNCTION CalculateModifiedShootingRating(me,enemy,range)
{
	int ret;

	if (range != -1)
		{
			ret = GetShootingWeaponModifier(me, enemy, range, 3, 1, 0);
		}
	else
		{
			ret = GetShootingWeaponModifier(me, enemy, 1, 3, 1, 0) * 2;
			ret += GetShootingWeaponModifier(me, enemy, 2, 3, 1, 0);
			ret += GetShootingWeaponModifier(me, enemy, 4, 3, 1, 0);
			ret /= 4;
		}

	// Adjust for strength of unit. (Maximum shooting strength = 6)
	ret *= GetCombatStrength(me);
	ret /= 600;

	// Check for reduced shooting power for some mounted
	ret *= MountedShootingModifier(me);
	ret /= 100;

	// Adjust for morale and disorder
	ret *= GetMoraleDisorderModifier(me, -1, 0);
	ret /= 100;

	// Adjust for cover (assume target stationary)
	if (enemy != -1)
		{
			ret *= CalculateCoverModifier(me, enemy, GetUnitX(enemy), GetUnitY(enemy), 1);
			ret /= 100;
		}

	return ret;
}

// Returns 1 if target enfiladed, otherwise 0
FUNCTION TargetEnfiladed(me, target)
{
	int ret;
	int angle;

	ret = 0;

	angle = GetAngleFrom(me, target);

	if ((angle >= 87) && (angle < 138)) // Probably.
		{
			ret = 1;
		}

	return ret;
}


// Returns 1 if there is another unit (or heavy fortifications, or a close combat or pursuit) blocking line of fire, 0 if not.
// Only artillery can ever shoot overhead. They can do so if (1) The artillery are on higher ground and the blockers aren't immediately in front of the shooters.
// (2) The blockers are (friendly or enemy) Light Foot separated by at least 2 squares from the shooters. (3) The blockers are enemy artillery and the shooters are heavy or medium artillery.

FUNCTION UnitBlockingLOS(me, target)
{
	int ret;
	int targetX;
	int targetY;
	int cost1;
	int cost2;

	targetX = GetUnitX(target);
	targetY = GetUnitY(target);

	ret = 1;

	BuildTileLine(GetUnitX(me), GetUnitY(me), GetUnitX(target), GetUnitY(target));

	ret = CheckRouteForBlockingUnit(me);

	if (ret == 0)
		{
			if (CheckRouteDiagonalStepNotBlocked(me, 3) == -2)
				{
					ret = 1;
				}
		}

	if (ret == 1)
		{
			cost1 = GetRouteCost(me, targetX, targetY, 0, 130); // Check route without avoiding units.

			if (cost1 >= 0)
				{
					ret = CheckRouteForBlockingUnit(me);

					if (ret == 0)
						{
							if (CheckRouteDiagonalStepNotBlocked(me, 3) == -2)
								{
									ret = 1;
								}
						}

					// If first route is blocked by troops, see if there is an equal length alternative route avoiding units.
					if (ret == 1)
						{
							cost2 = GetRouteCost(me, targetX, targetY, 0, 129); // Check route avoiding units. (It needs to be checked because GetRouteCost() won't avoid units if it has to deviate more than 1 square);

							if (cost1 == cost2)  // If route avoiding units is no longer
								{
									ret = CheckRouteForBlockingUnit(me);

									if (ret == 0)
										{
											if (CheckRouteDiagonalStepNotBlocked(me, 3) == -2)
												{
													ret = 1;
												}
										}
								}

						}
				}
		}

	return ret;
}

// Helper function for UnitBlockingLOS() function. Depends on route having already been set by GetRouteCost() in that function.
// Also checks for heavy fortifications blocking line of fire.
FUNCTION CheckRouteForBlockingUnit(me)
{
	int ret;
	int shooterX;
	int shooterY;
	int shooter_height;
	int blocker;
	int blocker_height;
	int routeX;
	int routeY;
	int target_index;
	int i;

	shooterX = GetUnitX(me);
	shooterY = GetUnitY(me);
	shooter_height = GetTileHeight(shooterX, shooterY);

	ret = 0;

	target_index = GetCheckRouteLength() - 1;

	for (i = 1; i < target_index; i++)  // Check route apart from shooter position and target position
	{
		routeX = GetCheckRouteX(i);
		routeY = GetCheckRouteY(i);

		if (GetTerrainCoverValue(routeX, routeY, 3) == 3) // Heavy fortifications
			{
				ret = 1;
			}
		else
			{
				blocker = GetUnitOnTile(routeX, routeY);

				if (blocker != -1)
					{
						ret = 1;

						if (IsArtillery(me) == 1) // Artillery can shoot overhead
							{
								ret = 0;

// Old version which only allowed artillery to shoot overhead in certain circumstances
//                // If blockers are not immediately in front of shooters, and shooters are on higher ground, not blocked
//                blocker_height = GetTileHeight(routeX, routeY);
//                if ((i > 1) && (shooter_height > blocker_height))
//                  {
//                    ret = 0;
//                  }
//                // If blockers are friendly or enemy LF who are separated from the shooters by at least 2 squares, not blocked
//                if (IsUnitSquadType(blocker, "Light_Foot") == 1)
//                  {
//                    if (i > 2)
//                      {
//                        ret = 0;
//                      }
//                  }
//                // If shooters are Heavy Artillery and blockers are enemy artillery, not blocked
//                if (GetAttrib(me, "Heavy_Artillery") > 50)
//                  {
//                    if ((IsArtillery(blocker) == 1) && (GetUnitSide(me) != GetUnitSide(blocker)))
//                      {
//                        ret = 0;
//                      }
//                  }
							}
							
						// v1.0.2 change - bows can shoot over other units from higher ground.
						if (GetAttrib(me, "Bow") > 34) // To exclude levy skirmishers
							{
								blocker_height = GetTileHeight(routeX, routeY);
								if (shooter_height > blocker_height)
									{
										ret = 0;
									}								
							}
					}
			}

		if (ret == 1) // If found a blocker, jump out of for loop
			{
				i = target_index;
			}
	}

	return ret;
}

// Returns unit's close combat rating. If enemy == -1 it returns a general purpose rating, otherwise a specific rating against the specified enemy. The resulting rating is only used for AI purposes, not to calculate results of combat.
// Does not take into account losses, cohesion loss or disorder.
FUNCTION CalculateCloseCombatRating(me, enemy)
{
	int ret;
	int net_impact_poa;
	int impact_rating;
	int net_melee_poa;
	int melee_rating;
	int strength;
	int difference;
	int POA_percent_multiplier;

	// This multiplier is used twice in this function and also twice in CalculateModifiedCloseCombatRating(). It may need tweaking in both functions.
	// It determines how much the close combat rating is affected by POAs.
	POA_percent_multiplier = 16;

	// Artillery, train and boats have no close combat rating
		if ((IsUnitSquadType(me, "Train") == 1) || (IsUnitSquadType(me, "Boat") == 1))
		{
			ret = 0;
		}
	else
		{
			// Uses non-charging rating. Use CalculateModifiedCloseCombatRating() if need to take into account disadvantage of charging enemy if that loses POA.
			// Also note that it is effectively calculating the close combat POAs on the basis of the units' current positions, even if one of them would have to move for close combat to occur
			if (enemy != -1)
				{
					net_impact_poa = GetImpactPOA(me, enemy, 0, 0, 1) - GetImpactPOA(enemy, me, 0, 0, 1);
					net_melee_poa = GetMeleePOA(me, enemy, 0, 0, 1) - GetMeleePOA(enemy, me, 0, 0, 1);
					// Log("Unit, Enemy, Net Impact POA, Net Melee POA", me, enemy, net_impact_poa, net_melee_poa);
				}
			else
				{
					POA_percent_multiplier *= 2; // To compensate for lack of netting out of POAs.
					net_impact_poa = GetImpactPOA(me, -1, 0, 0, 1) - 100; // deduct 100 to cancel out average enemy POAs.
					net_melee_poa = GetMeleePOA(me, -1, 0, 0, 1) - 100; // deduct 100 to cancel out average enemy POAs.
				}

			// Constrain POAs within bounds
			net_impact_poa = Min(net_impact_poa, 200);
			net_impact_poa = Max(net_impact_poa, -200);
			net_melee_poa = Min(net_melee_poa, 200);
			net_melee_poa = Max(net_melee_poa, -200);

			// Calculate ratings based on relative effect of POA - may need to be weighted more
			impact_rating = net_impact_poa * POA_percent_multiplier;
			impact_rating += 10000;
			impact_rating /= 100;

			melee_rating = net_melee_poa * POA_percent_multiplier;
			melee_rating += 10000;
			melee_rating /= 100;

			// Adjust melee rating (but not impact rating) for size of unit, not taking into account losses. Halve the difference between actual size and 600, which is max combat strength.
			// This isn't quite the same as the way the actual combat resolution script works out relative strengths, but it comes close for AI purposes.
			strength = Min(GetAttrib(me, "UnitSize"),600);
			difference = 600 - strength;
			difference /= 2;
			strength += difference;
			melee_rating *= strength
			melee_rating /= 600;

			// Weight towards melee rating
			ret = 2 * melee_rating;
			ret += impact_rating;
			ret /= 3;

		}

	// DEBUG
	//if (enemy > -1)
	//  {
	//    Log("Close Combat Rating", ret);
	//  }
	// END DEBUG

	return ret;
}

// Returns unit's close combat rating against an individual enemy unit, taking into account own morale and disorder. The resulting rating is only used for AI purposes, not to calculate results of combat.
// If enemy == -1, uses average POAs.
// charger == -1 if neither unit is charging, == id of assaulting unit if either is assaulting
// If melee_only flag set, reports melee rating only, otherwise reports rating based on 1/3 impact, 2/3 melee.
FUNCTION CalculateModifiedCloseCombatRating(me, enemy, charger, melee_only)
{
	int ret;
	int net_impact_poa;
	int impact_rating;
	int net_melee_poa;
	int melee_rating;
	int charging;
	int strength;
	int enemy_strength;
	int difference;
	int POA_percent_multiplier;

	// This multiplier is used twice in this function and also twice in CalculateCloseCombatRating(). It may need tweaking in both functions.
	// It determines how much the close combat rating is affected by POAs.
	POA_percent_multiplier = 16;

	// Train and boats have no close combat rating
	if ((IsUnitSquadType(me, "Train") == 1) || (IsUnitSquadType(me, "Boat") == 1))
		{
			ret = 0;
		}
	else
		{
			// Note that it is effectively calculating the close combat POAs on the basis of the units' current positions, even if one of them would have to move for close combat to occur
			if (enemy != -1)
				{
					charging = 0;
					if ((charger == me) || (GetAttrib(me,"Attacking") == 1))
						{
							charging = 1;
						}
					net_impact_poa = GetImpactPOA(me, enemy, charging, 0, 1);
					net_melee_poa = GetMeleePOA(me, enemy, charging, 0, 1);

					charging = 0;
					if ((charger == enemy) || (GetAttrib(enemy,"Attacking") == 1))
						{
							charging = 1;
						}
					net_impact_poa -= GetImpactPOA(enemy, me, charging, 0, 1);
					net_melee_poa -= GetMeleePOA(enemy, me, charging, 0, 1);
					// Log("Unit, Enemy, Net Impact POA, Net Melee POA", me, enemy, net_impact_poa, net_melee_poa);
				}
			else
				{
					POA_percent_multiplier *= 2; // To compensate for lack of netting out of POAs.
					net_impact_poa = GetImpactPOA(me, -1, GetAttrib(me, "Attacking"), 0, 1) - 100; // deduct 100 to cancel out average enemy POAs.
					net_melee_poa = GetMeleePOA(me, -1, GetAttrib(me, "Attacking"), 0, 1) - 100; // deduct 100 to cancel out average enemy POAs.
				}

			// Constrain POAs within bounds
			net_impact_poa = Min(net_impact_poa, 200);
			net_impact_poa = Max(net_impact_poa, -200);
			net_melee_poa = Min(net_melee_poa, 200);
			net_melee_poa = Max(net_melee_poa, -200);

			// Calculate ratings based on relative effect of POA - may need to be weighted more
			impact_rating = net_impact_poa * POA_percent_multiplier;
			impact_rating += 10000;
			impact_rating /= 100;

			melee_rating = net_melee_poa * POA_percent_multiplier;
			melee_rating += 10000;
			melee_rating /= 100;

			// Adjust melee rating (but not impact rating) for strength of unit (and enemy if known).
			strength = GetCombatStrength(me);
			if (enemy == -1)
				{
					// This isn't the same as the way the actual combat resolution script works out relative strengths, but is an estimate when enemy unit unknown.
					difference = 600 - strength;
					difference /= 2;
					strength += difference;
				}
			else
				{
					// This is similar to the way it is calculated in ResolveCloseCombat(), but does not take into account additional enemies
					enemy_strength = GetCombatStrength(enemy);

					if (strength > enemy_strength)
						{
							difference = strength - enemy_strength;
							difference /= 2;
							strength -= difference;
						}
				}

			melee_rating *= strength;
			melee_rating /= 600;

			if (melee_only == 1)
				{
					ret = melee_rating;
				}
			else
				{
					// Weight towards melee rating
					ret = 2 * melee_rating;
					ret += impact_rating;
					ret /= 3;
				}

			// Modify for morale and disorder
			charging = 0;
			if ((charger == me) || (GetAttrib(me,"Attacking") == 1))
				{
					charging = 1;
				}
			ret *= GetMoraleDisorderModifier(me, enemy, charging);
			ret /= 100;
		}

	// DEBUG
//  if (enemy > -1)
//    {
//      Log("Unit, enemy, charger, melee only?", me, enemy, charger, melee_only);
//      Log("Unit, net Impact POA, net Melee POA, Close Combat Rating", me, net_impact_poa, net_melee_poa, ret);
//    }
	// END DEBUG

	return ret;
}

// Returns 1 if unit would be permitted to charge enemy (NOT taking into account morale or move distance), otherwise 0.
// Current version only takes into account troops that can never charge.
// Needs additional functionaly to be added to take into account troop types that can sometimes charge in some cicumstances.
FUNCTION ChargePermitted(me, enemy)
{
	int ret;

	ret = 1;

	// These types can never charge
//  if ((IsArtillery(me) == 1) || (IsUnitSquadType(me,"Battle_Wagons") == 1) || (IsUnitSquadType(me,"Train") == 1) || (IsUnitSquadType(me,"Boat") == 1))
	if ((IsArtillery(me) == 1) || (IsUnitSquadType(me,"Train") == 1) || (IsUnitSquadType(me,"Boat") == 1))
		{
			ret = 0;
		}

	// Foot (other than keils) cannot charge mounted (other than light troops)
//  if ((IsFoot(me) == 1) && (IsMounted(enemy) == 1))
//    {
//      if ((PercentKeil(me) == 0) && (IsLightTroops(enemy) == 0))
//        {
//          ret = 0;
//        }
//    }

	if ((IsLightTroops(enemy) == 0) && (IsArtillery(enemy) == 0) && (IsUnitSquadType(enemy, "Train") == 0) && (IsOpen(GetUnitX(enemy), GetUnitY(enemy)) == 1))
		{
			// Light foot cannot charge unbroken non-artillery non-train non-light troops in open
			if (IsUnitSquadType(me, "Light_Foot") == 1)
				{
					if (GetAttrib(enemy, "MoraleState") < 3)
						{
							ret = 0;
						}
				}
		}


	return ret;
}

// Returns 0 if target not in angle of fire, 1 if only in AoF for left half of unit, 2 if only right half, 3 if in Aof for full unit.

FUNCTION ArcOfFire(me, target)
{
	return ArcOfFireToTile(me, GetUnitX(target), GetUnitY(target));
}

// Returns 0 if target tile not in angle of fire, 1 if only in AoF for left half of unit, 2 if only right half, 3 if in Aof for full unit.
// Note that this can be drastically simplifed if we only allow a unit to shoot at one target and hence don't need to keep track of whether we are shooting with left or right volley.

FUNCTION ArcOfFireToTile(me, x, y)
{
int angle;
int facing;
int volley;
int new_facing1;
int new_facing2;
int new_angle1;
int new_angle2;

	facing = GetUnitFacing(me);

	// Get angle
	angle = GetAngleFromTile(x, y, me);

	// DEBUG
	//StartString() ;
	//PrintStringLiteral("\nFACING = ");
	//PrintInt(facing);
	//PrintStringLiteral(", ANGLE = ") ;
	//PrintInt(angle) ;
	//AddTileTooltip() ;
	// END DEBUG

	if (IsUnitSquadType(me, "Ship") == 0)
		{
			if (angle > 47)
				{
					volley = 0;
				}
			else
				{
					if (angle < 23)
					// The idea is thast you should be able to shoot at full effect against the square 3 squares away and offset one, but not against the squares 1 or 2 squares away and offset one.
						{
							volley = 3;
						}
					else
						{
							// Clunky way to determine whether enemy is to left or right of straight ahead.
							new_facing1 = facing + angle;
							if (new_facing1 > 359)
								{
									new_facing1 -= 360;
								}

							new_facing2 = facing - angle;
							if (new_facing2 < 0)
								{
									new_facing2 += 360;
								}

							SetUnitFacing(me, new_facing1, 1);
							new_angle1 = GetAngleFromTile(x, y, me);
							SetUnitFacing(me, new_facing2, 1);
							new_angle2 = GetAngleFromTile(x, y, me);
							SetUnitFacing(me, facing, 1);

							if (new_angle1 < new_angle2)
								{
									volley = 1;
								}
							else
								{
									volley = 2;
								}

							// Log("Angle, NA1, NA2, volley", angle, new_angle1, new_angle2, volley);
						}
				}
		}
	else
		{
			if ((angle < 42) || (angle > 138))
				{
					volley = 0;
				}
			else
				{
					volley = 3;
				}
		}


//  if (volley == 0) // If not wanted on main tooltip, should be if ((volley == 0) && (UseAdvancedTT() == 1))
//    {
//      StartString() ;
//      PrintString("IDS_OUTOFARC");
//      PrintStringLiteral("\n");
//      AddTileTooltip() ;
//    }

	return volley;
}

// Returns 1 if unit will Pursue, otherwise 0
// Type 0 = initial pursuit. Type 1 = follow on pursuit. Type 2 = Pursuit off playable area.
// Assumes that units are still in adjacent squares prior to any pursuit
// All chances tweakable
FUNCTION WillUnitPursue(me, enemy, type)
{
	int ret;
	int stopChance;
	int obstacle;

	ret = 1;

	if (type == 0)
		{
			if (IsFoot(me) == 1)
				{
					// Foot normally don't pursue mounted
					if (IsMounted(enemy) == 1)
						{
							stopChance = 90; // May need tweaking
						}
					else
						{
							if ((IsLightTroops(me) == 0) && (IsLightTroops(enemy) == 1))
								{
									stopChance = 90; // May need tweaking
								}
							else
								{
									// Lower chance of pursuing if unit did not initiate the combat.
									if (GetAttrib(me,"Attacking") == 1)
										{
											stopChance = 0; // May need tweaking.
										}
									else
										{
											stopChance = 75; // May need tweaking
										}
								}
						}

					// Infantry who are neither warbands nor raw will not pursue
					if ((GetAttrib(me, "Impact_Foot") < 20) || ((IsUnitSquadType(me, "Warriors") == 0) && (IsUnitSquadType(me, "Undrilled_Heavy_Foot") == 0)))
						{
							if (GetBaseAttrib(me, "Experience") > 60) // UI reports Raw up to and including 60.
								{
									stopChance = 100;
								}
							else
								{
									stopChance = Max(stopChance, 75);
								}
						}

					// Foot usually don't pursue across an obstacle they are defending, and are even less likely to if the enemy are mounted or lights
					if (GetAttrib(me, "Attacking") == 0)
						{
							obstacle = IsTileEdgeDefendibleObstacle(GetUnitX(me), GetUnitY(me), GetUnitX(enemy), GetUnitY(enemy));
							if (obstacle > -1)
								{
									if ((IsMounted(enemy) == 1) || (GetTerrainCoverValue(GetUnitX(me), GetUnitY(me), 3) == 3)) // never pursue if enemy is mounted, or if defending heavy fortifications.
										{
											stopChance = 100;
										}
									else
										{
											if (IsLightTroops(enemy) == 1)
												{
													stopChance = Max(stopChance, 95); // May need tweaking
												}
											else
												{
													stopChance = Max(stopChance, 85); // May need tweaking
												}
										}
								}
						}
				}

			// Troops in square do not pursue
			if (GetAttrib(me, "InSquare") == 1)
				{
					stopChance = 100;
				}

			// Battle wagons and artillery do not pursue
//      if ((IsUnitSquadType(me, "Battle_Wagons") == 1) || (IsArtillery(me) == 1))
			if (IsArtillery(me) == 1)
				{
					stopChance = 100;
				}
		}

	if (type > 0)
		{
			stopchance = 50; // May need tweaking

			// Foot won't continue to pursue mounted and will rarely continue to pursue at all
			if (IsFoot(me) == 1)
				{
					if (IsMounted(enemy) == 1)
						{
							stopChance = 100; // May need tweaking
						}
					else
						{
							stopChance = 75; // May need tweaking
						}
				}

			//  Knights tend to keep pursuing
//      if (IsUnitSquadType(me, "Knights") == 1)
//        {
//          stopChance = 25;
//        }
		}

	if (Rand(1,100) <= stopChance)
		{
			ret = 0;
		}

	Log("Unit, chance of stopping pursuit, will pursue?", me, stopChance, ret);

	if (ret == 1)
		{
			if (type == 0) // Initial pursuit
				{
					if ((IsFoot(me) == 1) && (GetBaseAttrib(me, "Experience") <= 60)) // Raw foot
						{
							PrintStringLiteralX(1, 0, "\n");
							PrintStringX(1,0, "IDS_INEXPERIENCED_PURSUE1");
							PrintStringLiteralX(1,0, " ");
							PrintUnitPossession(1, 0, me, 2);
							PrintStringLiteralX(1,0, " ");
							PrintStringX(1,0, "IDS_INEXPERIENCED_PURSUE2");
							PrintStringLiteralX(1,0, ".");

							PrintStringLiteralX(2, 0, "\n");
							PrintStringX(2,0, "IDS_INEXPERIENCED_PURSUE1");
							PrintStringLiteralX(2,0, " ");
							PrintUnitPossession(2, 0, me, 2);
							PrintStringLiteralX(2,0, " ");
							PrintStringX(2,0, "IDS_INEXPERIENCED_PURSUE2");
							PrintStringLiteralX(2,0, ".");
						}
				}
		}

	return ret;
}

// Returns 1 if unit will Pursue, otherwise 0
// Type 0 = initial pursuit. Type 1 = follow on pursuit. Type 2 = Pursuit off playable area.
// Assumes that units are still in adjacent squares prior to any pursuit
// All chances tweakable
FUNCTION OldWillUnitPursue(me, enemy, type)
{
	int ret;
	int stopChance;
	int obstacle;

	ret = 1;

	if (type == 0)
		{
			if (IsFoot(me) == 1)
				{
					// Foot normally don't pursue mounted
					if (IsMounted(enemy) == 1)
						{
							stopChance = 90; // May need tweaking
						}
					else
						{
							if ((IsLightTroops(me) == 0) && (IsLightTroops(enemy) == 1))
								{
									stopChance = 90; // May need tweaking
								}
							else
								{
									// Lower chance of pursuing if unit did not initiate the combat.
									if (GetAttrib(me,"Attacking") == 1)
										{
											stopChance = 0; // May need tweaking.
										}
									else
										{
											stopChance = 75; // May need tweaking
										}
								}
						}

					// Foot usually don't pursue across an obstacle they are defending, and are even less likely to if the enemy are mounted or lights
					if (GetAttrib(me, "Attacking") == 0)
						{
							obstacle = IsTileEdgeDefendibleObstacle(GetUnitX(me), GetUnitY(me), GetUnitX(enemy), GetUnitY(enemy));
							if (obstacle > -1)
								{
									if ((IsMounted(enemy) == 1) || (GetTerrainCoverValue(GetUnitX(me), GetUnitY(me), 3) == 3)) // never pursue if enemy is mounted, or if defending heavy fortifications.
										{
											stopChance = 100; // May need tweaking
										}
									else
										{
											if (IsLightTroops(enemy) == 1)
												{
													stopChance = 95; // May need tweaking
												}
											else
												{
													stopChance = 85; // May need tweaking
												}
										}
								}
						}

					// Raw troops and Warrior or Undrilled Heavy Foot Impact Foot are more likely to pursue
					if ((GetBaseAttrib(me, "Experience") <= 50) || (((IsUnitSquadType(me, "Warriors") == 1) || (IsUnitSquadType(me, "Undrilled_Heavy_Foot") == 1)) && (GetAttrib(me, "Impact_Foot") > 20)))
						{
							stopChance = Max(0, stopChance - 20); // May need tweaking
						}

				}

			// Troops in square do not pursue
			if (GetAttrib(me, "InSquare") == 1)
				{
					stopChance = 100;
				}

			// Battle wagons and artillery do not pursue
//      if ((IsUnitSquadType(me, "Battle_Wagons") == 1) || (IsArtillery(me) == 1))
			if (IsArtillery(me) == 1)
				{
					stopChance = 100;
				}
		}

	if (type > 0)
		{
			stopchance = 50; // May need tweaking

			// Foot normally don't pursue mounted
			if ((IsFoot(me) == 1) && (IsMounted(enemy) == 1))
				{
					stopChance = 80; // May need tweaking
				}

			//  Knights tend to keep pursuing
//      if (IsUnitSquadType(me, "Knights") == 1)
//        {
//          stopChance = 25;
//        }

			// So do Warrior Impact Foot. (Note that this overrides the foot tendency not to pursue mounted for follow-on pursuits if the warriors have initially pursued and maintained contact)
			if (((IsUnitSquadType(me, "Warriors") == 1) || (IsUnitSquadType(me, "Undrilled_Heavy_Foot") == 1)) && (GetAttrib(me, "Impact_Foot") > 20))
				{
					stopchance = 25; // May need tweaking
				}
		}

	if (Rand(1,100) <= stopChance)
		{
			ret = 0;
		}

	Log("Unit, chance of stopping pursuit, will pursue?", me, stopChance, ret);

	return ret;
}

// Checks whether Pursuer is still close enough. If so, damages router. If not, removes units from each others' close combat list.
FUNCTION CheckPursuerStillInContact(me, enemy)
{
	int enemyX;
	int enemyY;
	int distance;
	int required_distance;
	int killed;
	int survivors;
	int i;
	int id;

	if ((IsUnitValid(enemy) != 1) || (IsValidTile(GetUnitX(enemy), GetUnitY(enemy)) != 1))
		{
			RemoveCloseCombat(me, enemy);
		}
	else
		{
			if (IsMounted(me) == 1)
				{
					required_distance = 2;
				}
			else
				{
					required_distance = 1;
				}

			distance = GetDistanceBetween(me, enemy);

			// if pursuer still in contact - or, if mounted, 2 squares away
			if (distance <= required_distance)
				{
					// AddCloseCombat() commented out because it is better to have troops whose charge target routs before contact not pursue, rather than risk multiple units being added to
					// pursuer's close combat list - which otherwise happens if the routers remain within the required distance at the end of their rout move despite the pursuer having switched target.
					// Can't think of a test for this situation that could not be confounded by VizQ sequencing issues.
					// was: // If routers not yet in pursuers "Enemy" array, add them - occurs if fragged unit routs when charged.
					//AddCloseCombat(me, enemy); // Checks if the units are already in each other's "Enemy" attrib array, and if not, adds them

					// Pursuers slay some routers - not many because we want pursuit to continue as long as possible for some types
					killed = StartingStrength(enemy) * 100;
					killed /= GetAttrib(enemy, "UnitSize");
					killed *= Rand(50, 150);
					killed /= 400;
					killed /= distance; // Reduce casualties if not in adjacent tile
					killed = Min(killed, GetAttrib(enemy, "TotalMen"));
					DisplayCasualties(enemy, killed, 0, 1);
					survivors = GetAttrib(enemy, "TotalMen") - killed;
					SetAttrib(enemy, "TotalMen", survivors);
					Log("Routers. Pursuers. Killed routers. Surviving routers", enemy, me, killed, survivors);

					TestForGeneralLost(enemy, 0, 1, GetCurrentSide());
					DoProximityMoraleTests(GetUnitSide(enemy));

					// test to see if router is destroyed
					if (survivors < StartingStrength(enemy) / 20) // Not enough left to bother to pursue further
						{
							DisperseRouters(enemy);
						}
					else
						{
							SetUnitStatusFlag(enemy); // Update losses icon
						}
				}
			else // Pursuer has lost contact, so remove the units from each other's close combat list
				{
					RemoveCloseCombat(me, enemy); // Note that it should not matter if there isn't actually any close combat to remove
				}
		}
}

// Check to see whether current charge target is priority charge target. Returns -1 if current_target is -1 and there is no other priority target.
// Checks square directly ahead of the unit for a target. If there is no unit (friendly or enemy) on that square, looks in the next square ahead.
// An enemy unit directly ahead only counts as priority if within 45 degrees of facing the charger.
// If no other priority target found, returns the original value of current_target. It is possible for more than one unit to qualify as a priority target.
// If the current_target does not qualify, returns the first ZOCing one found if any, otherwise the one to the front
// However, note that if current_target is -1, the function will preferentially return the frontal target.

FUNCTION PriorityChargeTarget(me, current_target)
{
	int x;
	int y;
	int facing;

	x = GetUnitX(me);
	y = GetUnitY(me);
	facing = CorrectedFacing(me);

	return PriorityChargeTargetAtTile(x, y, facing, me, current_target);
}

// Helper function for PriorityChargeTarget(), but can also be used to determine hypothetical priority target if "me" was at the specified tile, with the specified facing.
// Used thus in Check_Unit_Assault() to check whether charge target qualifies as charge target at end of charge as well as at beginning.
FUNCTION PriorityChargeTargetAtTile(x, y, facing, me, current_target)
{
	int priority_target;
	int j;
	int k;
	int ret;

	ret = -1;

	if (ret == -1)
		{
			// Check for ZOCers
			for (j = x-1; j <= x+1; j++)
			{
				for (k = y-1; k <= y+1; k++)
				{
					priority_target = IsTileZOCing(me, x, y, j, k, 1, 0);
					if (priority_target >= 0)
						{
							ret = priority_target;
							if (ret == current_target)
								{
								  // Jump out of loops
									j = x + 2; 
									k = y + 2;
								}
						}
				}
			}
		}

	if (ret == -1)
		{
			ret = current_target;
		}

	// Log("Unit, Priority Target", me, ret);

	return ret;
}

// Helper function for PriorityChargeTarget(), but can also be used to determine hypothetical priority target if "me" was at the specified tile, with the specified facing.
// Used thus in Check_Unit_Assault() to check whether charge target qualifies as charge target at end of charge as well as at beginning.
FUNCTION OldPriorityChargeTargetAtTile(x, y, facing, me, current_target)
{
	int potential_target;
	int frontalX;
	int frontalY;
	int current_target_qualifies;
	int priorityTarget1;
	int priorityTarget2;
	int j;
	int k;
	int ret;

	ret = -1;

	priorityTarget1 = -1;
	priorityTarget2 = -1;

	current_target_qualifies = 0;

	frontalX = AdjacentX(x, facing);
	frontalY = AdjacentY(y, facing);

	potential_target = GetUnitOnTile(frontalX, frontalY);
	// If no unit there, look in the next tile forward (orthogonal directions only). (So that units in chequerboard formation can protect each other from being ganged up on)
	// I am not entirely sure about reducing the distance to 1 square in diagonal directions.
	// However, making it 2 squares diagonally looks odd on the map, it just looks too far away to be the priority target.
	// It might also cause issues with the method (see below) of checking for the diagonal move being blocked by an existing combat if "me" is not already in the adjacent square.
	// On the other hand , making it 1 square diagonally means that the effects of a chequerboard are lost in diagonal directions.
	// However, as the battle lines are normally drawn up orthogonally, I think that probably doesn't matter as much as getting it to look right.
	// Of course, I can easily change it back if it causes problems.
	if (potential_target == -1)
		{
			if ((facing == 0) || (facing == 90) || (facing == 180) || (facing == 270)) // Orthogonal directions
				{
					potential_target = GetUnitOnTile(AdjacentX(frontalX, facing), AdjacentY(frontalY, facing));
				}
		}

	// Log("Facing before turn, unit in that direction", facing, potential_target);

	if (potential_target > -1)
		{
			if ((GetUnitSide(potential_target) != GetUnitSide(me)) && (GetAttrib(potential_target, "MoraleState") < 3))
				{
					// If potential target facing within 45 degrees of directly towards us
					if (GetAngleFromTile(x, y, potential_target) < 50)
						{

							// If potential target is in close combat then it isn't a priority target if it isn't a permitted charge target.

							if ((IsInCloseCombat(potential_target) == 1) && (ChargePermitted(me, potential_target) == 0))
								{
									potential_target = -1;
								}

							// If potential target is blocked off from the testing unit by a diagonal close combat, then it isn't a priority target.
							// Cannot use if (CallUnitFunctionDirect(me, "CHECK_UNIT_ASSAULT", me, potential_target) < 0) as test because it causes a crash bug - presumably due to recursive function calls
							// The test below may not work perfectly for units that have to move to get to the adjacent position - if the program finds a different route, which is unlikely.
							// Attempting to shift the unit before testing will cause bugs owing to this function being called from Check_Unit_Assault().

							GetRouteCost(me, GetUnitX(potential_target), GetUnitY(potential_target), 0, 1);
							if (CheckRouteDiagonalStepNotBlocked(me, 4) == -2)
								{
									potential_target = -1;
								}


							if (potential_target != -1)
								{
									// Non light troops ignore light troops
									if ((IsLightTroops(potential_target) == 0) || (IsLightTroops(me) == 1))
										{
											if ((current_target == -1) || (potential_target == current_target))
												{
													ret = potential_target;
												}
											else
												{
													priorityTarget1 = potential_target;
												}
										}
								}
						}
				}
		}

	if (ret == -1)
		{
			// If current_target has not yet qualified as a priority target, check for ZOCers
			for (j = x-1; j <= x+1; j++)
			{
				for (k = y-1; k <= y+1; k++)
				{
					potential_target = IsTileZOCing(me, x, y, j, k, 1, 0);
					if (potential_target >= 0)
						{
							if ((current_target == -1) || (potential_target == current_target))
								{
									ret = potential_target;
								}
							else
								{
									if (priorityTarget2 == -1)
										{
											priorityTarget2 = potential_target;
										}
								}
						}
				}
			}
		}

	if (ret == -1)
		{
			if (priorityTarget2 >= 0)
				{
					ret = priorityTarget2;
				}
			else
				{
					if (priorityTarget1 >= 0)
						{
							ret = priorityTarget1;
						}
					else
						{
							ret = current_target;
						}
				}
		}

	// Log("Unit, Priority Target", me, ret);

	return ret;
}


// Adds additional close combat opponents to REP Queue
FUNCTION AddOtherOpponentsToREPQueue(router, primary_pursuer)
{
	int i;
	int opponent;

	for (i = 0; i < 8; i++)
	{
		opponent = GetAttribArray(router, "Enemy", i);

		if (opponent != -1)
			{
				// If not in close combat with another unit,  victors pursue routers (if appropriate)
				if (MightPursue(opponent) == 0)
					{
						RemoveCloseCombat(router, opponent);
					}
				else
					{
						// if not principal pursuer, which has already been added to REP Queue if it is pursuing. (If not, another pursuer will become main pursuer, by being first pursuer in REP Queue)
						if (opponent != primary_pursuer)
							{
								if (WillUnitPursue(opponent, router, 0) == 1)
									{
										SetAttrib(opponent, "AnimSituation", 6); // Pursuit. Note: Set outside of AddToREPQueue() because we don't want it set for chargers pursuing evaders.
										AddToREPQueue(router, opponent);
										SetAttrib(opponent,"AP",GetBaseAttrib(opponent,"AP"));
									}
								else
									{
										RemoveCloseCombat(router, opponent);
									}
							}
					}
			}
	}
}

// Returns combat strength multiplier for additional enemies (%). plus == 1 version takes into account unit considering charging (for SetAssaultString()).
FUNCTION AdditionalEnemiesModifier(me, plus)
{
	int modifier;
	int additional_enemies;
	int adjustment_constant;
	int minimum_modifier_constant;

	// These constants are used to determine strength reduction for fighting multiple units. They are a prime candidate for tweaking.
	// Note that as a percentage reduction in combat effectiveness per extra enemy unit fighting the unit, the relative reduction will effectively be halved in practice,
	// because the difference between larger and smaller unit effective size is halved.
	adjustment_constant = 20;
	minimum_modifier_constant = 50;

	modifier = 100;

	additional_enemies = NumberOfCloseCombatOpponents(me) - 1;

	if (plus == 1)
		{
			additional_enemies += 1;
		}

	if (additional_enemies > 0)
		{
			modifier = additional_enemies * adjustment_constant; // Defined above - may need tweaking
			modifier = 100 - modifier;
			modifier = Max(modifier, minimum_modifier_constant); // Limit this effect. Also defined above, and might need tweaking.
		}

	// Log("Unit, number of opponents, strength modifier", me, additional_enemies + 1, modifier);

	return modifier;
}

// Returns the percent of Shot in the unit
FUNCTION PercentShooters(me)
{
	int ret;
	int bow;

//	ret = GetAttrib(me, "Handgun") + GetAttrib(me, "Bombs") + GetAttrib(me, "Bow") + GetAttrib(me, "Longbow") + GetAttrib(me, "Crossbow") + GetAttrib(me, "Sling") + GetAttrib(me, "Javelins") + GetAttrib(me, "Darts");
	
	ret = GetAttrib(me, "Handgun") + GetAttrib(me, "Bombs") + GetAttrib(me, "Longbow") + GetAttrib(me, "Crossbow") + GetAttrib(me, "Sling") + GetAttrib(me, "Javelins");
	
	bow = GetAttrib(me, "Bow");
	
  if (bow > 20)
  	{
  		ret += bow;
  	}

	return ret;
}

// Reduce relative shooting power of non-light mounted to 75% if 100% shooters, or pro-rata if more than 50% shooters. (Half and half units shoot at full effect).
FUNCTION MountedShootingModifier(me)
{
	int ret;
	int deduction;

	ret = 100;

	if ((IsMounted(me) == 1) && (IsUnitSquadType(me,"Light_Horse") == 0))
		{
			deduction = Max(0, PercentShooters(me) - 50);
			deduction /= 2;
			ret = 100 - deduction;
			// Log("Mounted Shooting Modifier", ret);
		}

	return ret;
}

// Returns Impact POA (* 100). If print == 1, prints information to current string. If test == 1 it prints directly to UIstring, otherwise via AddVizFunctionCall.
FUNCTION GetImpactPOA(me, enemy, charging, print, test)
{
	int percent;
	int POA;
	int percent_12ranks;
	int percent_16ranks;
	int percent_disordered;
	int percent_severely_disordered;
	int increment;
	int height_difference;
	int enemy_charging;
	int obstacleType;
	int any_printed;
	int multiplier;
	int strength;
	int ProtectionIncrementAlreadyReceived;

	any_printed = 0;
	ProtectionIncrementAlreadyReceived = 0;

	enemy_charging = charging + 1;
	enemy_charging = enemy_charging % 2;

	POA = 0;

	percent_disordered = Min(PercentDisordered(me, enemy, charging), 100);
	percent_severely_disordered = Max(PercentDisordered(me, enemy, charging) - 100, 0);

	// Pike
	percent = GetAttrib(me,"Pike");
	if (percent > 0)
		{
			percent_12ranks = Percent12RanksPike(me);
			percent_16ranks = Percent16RanksPike(me);

			if (percent_12ranks > 0) // If at least some of the unit is 12+ ranks deep
				{
					if (enemy == -1)
						{
							if (print == 1)
								{
									any_printed = 1;
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 18);
									PrintStringLiteralX(1, test, ": +");
									PrintIntX(1, test, percent_12ranks);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_UI_INFO_IMPACT_PIKE");
									PrintStringLiteralX(1, test, "\n");
								}

							if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
								{
									increment = 100 - percent_severely_disordered;
									increment *= percent_12ranks; // Basic POA only for pikes over 12 ranks deep (equivalent to 3 FOGAM ranks).
									increment /= 100;
									POA += increment;
								}

						}
					else
						{
							if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
								{
									if ((charging == 0) || (IsMounted(enemy) == 0) || (IsShockTroops(enemy) == 0)) // If not charging or enemy is not mounted shock troops
										{
											increment = 100 - percent_severely_disordered;
											increment *= percent_12ranks; // Basic POA only for pikes over 12 ranks deep (equivalent to 3 FOGAM ranks).
											increment /= 100;
											POA += increment;
											if ((print == 1) && (increment > 0))
												{
													if (any_printed == 1)
														{
															PrintStringLiteralX(1, test, ", ");
														}
													PrintStringIndexedX(1, test, "IDS_CAPABILITY", 18);
													PrintStringLiteralX(1, test, " +");
													PrintIntX(1, test, increment);
													any_printed = 1;
												}
										}
								}
						}


					if (percent_16ranks > 0)  // If at least some of the unit is 16+ ranks deep
						{
							if (enemy == -1)
								{
									if (print == 1)
										{
											any_printed = 1;
											PrintStringX(1, test, "IDS_UNIT_DEEP_PIKE");
											PrintStringLiteralX(1, test, ": +");
//                      PrintIntX(1, test, percent_16ranks / 2); // Max 50 POAs at impact.
											PrintIntX(1, test, percent_16ranks);
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_UI_INFO_IMPACT_DEEP_PIKE");
											PrintStringLiteralX(1, test, "\n");
										}

									// If unit has large block of pike/heavy weapon/swordsmen, is not disrupted or disordered, and it is in open terrain
									if (GetAttrib(me,"MoraleState") < 1)
										{
											if ((IsOpen(GetUnitX(me), GetUnitY(me)) == 1) && (percent_disordered == 0))
												{
//                          increment = percent_16ranks / 2; // Max 50 POAs at impact.
													increment = percent_16ranks;
													POA += increment;
												}
										}
								}
							else
								{
									if (GetAttrib(me,"MoraleState") < 1) // If not DISRUPTED
										{
											// If unit has large block of pike/heavy weapon/swordsmen, is not fragmented or severely disordered, and combat is in open terrain
											if ((IsCombatInOpen(me,enemy) == 1) && (percent_disordered == 0))
												{
//                          increment = percent_16ranks / 2; // Max 50 POAs at impact.
													increment = percent_16ranks;
													POA += increment;
													if ((print == 1) && (increment > 0))
														{
															if (any_printed == 1)
																{
																	PrintStringLiteralX(1, test, ", ");
																}
															PrintStringX(1, test, "IDS_UNIT_DEEP_PIKE");
															PrintStringLiteralX(1, test, " +");
															PrintIntX(1, test, increment);
															any_printed = 1;
														}
												}
										}
								}
						}
				}
		}

	// Impact Foot
	percent = GetAttrib(me,"Impact_Foot");
	if (percent >= 33) // Note that there should not really be any Impact Foot units with less than 50% Impact Foot.
		{
			percent_12ranks = Percent12RanksImpactFoot(me);
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 11);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_IMPACT_FOOT_FOOT");
							PrintStringLiteralX(1, test, " ");
							if (percent_12ranks > 0)
								{
									PrintStringLiteralX(1, test, "+");
									PrintIntX(1, test, percent_12ranks / 10);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_UI_INFO_IMPACT_IMPACT_FOOT_DEEP");
									PrintStringLiteralX(1, test, " ");
								}
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_IMPACT_FOOT_MOUNTED");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 150;  // average vs horse and foot
					POA += increment;
				}
			else
				{
					if (IsFoot(enemy) == 1)
						{
							increment = 200;
							POA += increment; // Note percent of impact foot does not affect this, as other troops with them also get the impact bonus vs foot.
							if ((print == 1) && (increment > 0))
								{
									if (any_printed == 1)
										{
											PrintStringLiteralX(1, test, ", ");
										}
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 11);
									PrintStringLiteralX(1, test, " +");
									PrintIntX(1, test, increment);
									any_printed = 1;
								}

							if (percent_12ranks > 0)
								{
									increment = 10; // Depth POA. If this is adjusted, it also needs adjusting in the enemy == -1 section
									increment *= percent_12ranks;
									increment /= 100;
									POA += increment;

									if ((print == 1) && (increment > 0))
										{
											if (any_printed == 1)
												{
													PrintStringLiteralX(1, test, ", ");
												}
											PrintStringX(1, test, "IDS_UNIT_DEEP_IMPACT_FOOT");
											PrintStringLiteralX(1, test, " +");
											PrintIntX(1, test, increment);
											any_printed = 1;
										}
								}
						}


					if ((IsMounted(enemy) == 1) || (IsUnitSquadType(enemy, "Elephants") == 1))
						{
							increment = 100;
							// Cancelled if the foot are charging against enemy mounted shock troops
							if ((IsShockTroops(enemy) == 1) && (charging == 1))
								{
									increment = 0;
								}

							POA += increment; // Note percent of impact foot does not affect this, as other troops with them also get the impact bonus vs foot.
							if ((print == 1) && (increment > 0))
								{
									if (any_printed == 1)
										{
											PrintStringLiteralX(1, test, ", ");
										}
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 11);
									PrintStringLiteralX(1, test, " +");
									PrintIntX(1, test, increment);
									any_printed = 1;
								}
						}
				}
		}

	// Protected Pikeless Foot Shooters. NOTE ** Will also need to exclude any other cases where Protection has already been taken into account. **
	// Also include artillery.
	if (enemy != -1)
		{
			if (IsFoot(me) == 1)
				{
					percent = PercentShooters(me);

					if (IsArtillery(me) == 1)
						{
							percent = 100;
						}

					if ((percent > 0) && (GetAttrib(me, "Pike") == 0))
						{
							if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
								{
									// If enemy is mounted and we are in terrain that confers protection
									if (IsMounted(enemy) == 1)
										{
											if (InProtectiveTerrain(me, enemy, charging) == 1)
												{
													increment = 100 - percent_severely_disordered;
													ProtectionIncrementAlreadyReceived = increment;
													POA += increment;
													if ((print == 1) && (increment > 0))
														{
															if (any_printed == 1)
																{
																	PrintStringLiteralX(1, test, ", ");
																}
															PrintStringX(1, test, "IDS_TT_PROTECTED");
															PrintStringLiteralX(1, test, " +");
															PrintIntX(1, test, increment);
															any_printed = 1;
														}
												}
										}
								}
						}
				}
		}

		// Artillery in the open
	if (IsArtillery(me) == 1)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringX(1, test, "IDS_UI_INFO_ARTILLERY");
						}
					
					increment = -100; // Average of Protected and Unprotected
					POA += increment;
				}
			else
				{
					if (InProtectiveTerrain(me, enemy, charging) != 1)
						{
							increment = -200;
							POA += increment;
							if (print == 1)
								{
									if (any_printed == 1)
										{
											PrintStringLiteralX(1, test, ", ");
										}
									PrintStringX(1, test, "IDS_TT_UNPROTECTED");
									PrintStringLiteralX(1, test, " ");
									PrintIntX(1, test, increment);
									any_printed = 1;
								}
						}
				}
		}

	// Heavy Weapon
	percent = GetAttrib(me, "Heavy_Weapon");
	if ((percent > 0) && (Percent16RanksPike(me) == 0))
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 10);
							PrintStringLiteralX(1, test, ": +");
							PrintIntX(1, test, percent);
							PrintStringLiteralX(1, test, " ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_HEAVY_WEAPON");

							PrintStringLiteralX(1, test, "\n");
						}

					increment = 50; // average vs horse and foot
				}
			else
				{
					increment = 0;

					if (IsFoot(enemy) == 1)
						{
							increment = 100;
						}
				}

			increment *= percent;
			increment /= 100;

			POA += increment;
			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 10);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}

		}

	// Light Spear
	percent = GetAttrib(me, "Light_Spear");
	if (percent >= 33)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 16);
							PrintStringLiteralX(1, test, ": ");
							if (IsFoot(me) == 1)
								{
									PrintStringX(1, test, "IDS_UI_INFO_IMPACT_LIGHT_SPEAR_FOOT");
								}
							else // Mounted
								{
									PrintStringX(1, test, "IDS_UI_INFO_IMPACT_LIGHT_SPEAR_MOUNTED");
								}

							PrintStringLiteralX(1, test, "\n");
						}

					if (IsFoot(me) == 1)
						{
							increment = 100; // average vs horse and foot
						}
					else // Mounted
						{
							increment = 50; // average vs horse and foot
						}
				}
			else
				{
					if (IsFoot(me) == 1)
						{
							increment = 100; // average vs horse and foot
						}
					else // Mounted
						{
							increment = 50; // average vs horse and foot
						}

					// Cancelled if foot light spears are charging against enemy mounted shock troops
					if ((IsFoot(me) == 1) && (IsMounted(enemy) == 1))
						{
							if ((IsShockTroops(enemy) == 1) && (charging == 1))
								{
									increment = 0;
								}
						}
				}

			POA += increment;
			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 16);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}

		}

	// Offensive Spearmen
	percent = GetAttrib(me, "Offensive_Spearmen");
	if (percent > 0)
		{
			// Take account of non-spearmen - usually archers - being in rear ranks beyond the unitsize 600 depth, so not reducing the spear POA.
			if (percent < 100)
				{
					strength = GetAttrib(me, "TotalMen") * 100 ;
					strength /= StartingStrength(me);
					strength *= GetAttrib(me, "UnitSize");
					strength /= 100;
					if (strength > 600)
						{
							percent *= strength;
							percent /= 600;
							percent = Min(percent, 100);
						}
				}

			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 24);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_SPEARMEN_PREAMBLE");
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, percent);
							PrintStringLiteralX(1, test, " ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_OFF_SPEARMEN");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 0;

					if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
						{
							increment = 100; // average vs horse and foot
							multiplier = Max(0, percent - percent_severely_disordered);
							increment *= multiplier;
							increment /= 100;
						}
				}
			else
				{
					increment = 0;

					// Only get POA if not FRAGMENTED or Severely Disordered
					if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
						{
							increment = 100;
							multiplier = Max(0, percent - percent_severely_disordered);
							increment *= multiplier;
							increment /= 100;
						}

					// Cancelled if the Spearmen are charging against enemy mounted shock troops
					if (IsMounted(enemy) == 1)
						{
							if ((IsShockTroops(enemy) == 1) && (charging == 1))
								{
									increment = 0;
								}
						}
				}

			POA += increment;
			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 24);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}

		}


	// Defensive Spearmen
	percent = GetAttrib(me, "Defensive_Spearmen");
	if (percent > 0)
		{
			// Take account of non-spearmen - usually archers - being in rear ranks beyond the unitsize 600 depth, so not reducing the spear POA.
			if (percent < 100)
				{
					strength = GetAttrib(me, "TotalMen") * 100 ;
					strength /= StartingStrength(me);
					strength *= GetAttrib(me, "UnitSize");
					strength /= 100;
					if (strength > 600)
						{
							percent *= strength;
							percent /= 600;
							percent = Min(percent, 100);
						}
				}

			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 22);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_SPEARMEN_PREAMBLE");
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, percent);
							PrintStringLiteralX(1, test, " ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_DEF_SPEARMEN");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 0;

					if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
						{
							increment = 90; // average vs horse and foot
							multiplier = Max(0, percent - percent_severely_disordered);
							increment *= multiplier;
							increment /= 100;
						}
				}
			else
				{
					increment = 0;

					// Only get POA if not FRAGMENTED or Severely Disordered
					if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
						{
							increment = 100;
							multiplier = Max(0, percent - percent_severely_disordered);
							increment *= multiplier;
							increment /= 100;
						}


					// Cancelled if charging unless the enemy are Defensive Spearmen
					if ((charging == 1) && (GetAttrib(enemy, "Defensive_Spearmen") == 0))
						{
							increment = 0;
						}
				}

			POA += increment;
			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 22);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}

		}
		
// Darts
	if ((IsFoot(me) == 1) && (IsLightTroops(me) == 0) && (GetAttrib(me,"Impact_Foot") == 0)) // Impact foot cannot get extra POA for darts.
		{
			percent = GetAttrib(me, "Darts");

			if (percent > 0)
				{
					percent += GetAttrib(me, "Bow"); // Bows also count toward Dart POA in unit with Darts
					percent = Min(100, percent);
					
					increment = percent * 2;
					increment = Min(increment, 100);

					if (enemy == -1)
						{
							if (print == 1)
								{
									any_printed = 1;
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 12);
									PrintStringLiteralX(1, test, ": +");
									PrintIntX(1, test, increment);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_UI_INFO_DARTS");
									PrintStringLiteralX(1, test, "\n");
								}

							POA += increment;
						}
					else
						{
							if (charging == 0)
								{
									// Reduce effect vs armoured or better foot, or heavily armoured cavalry
									if (((IsFoot(enemy) == 1) && (GetAttrib(enemy, "BodyArmour") >= 100)) || ((IsMounted(enemy) == 1) && (GetAttrib(enemy, "BodyArmour") >= 300)))
										{
											increment *= 2;
											increment /= 3;
										}

									POA += increment;
									if ((print == 1) && (increment > 0))
										{
											if (any_printed == 1)
												{
													PrintStringLiteralX(1, test, ", ");
												}
											PrintStringIndexedX(1, test, "IDS_CAPABILITY", 12);
											PrintStringLiteralX(1, test, ": +");
											PrintIntX(1, test, increment);
											any_printed = 1;
										}
								}
						}
				}
		}

//	// Support Shooting
//	if ((IsFoot(me) == 1) && (IsLightTroops(me) == 0))
//		{
//			percent = GetAttrib(me, "Bow") + GetAttrib(me, "Longbow") + GetAttrib(me, "Crossbow");
//			if (GetAttrib(me,"Impact_Foot") == 0)
//				{
//					percent += GetAttrib(me, "Darts");
//				}
//			if (percent > 0)
//				{
//					increment = percent * 2;
//					increment = Min(increment, 100);

//					if (enemy == -1)
//						{
//							if (print == 1)
//								{
//									any_printed = 1;
//									PrintStringX(1, test, "IDS_SUPPORT_SHOOTING");
//									PrintStringLiteralX(1, test, ": +");
//									PrintIntX(1, test, increment);
//									PrintStringLiteralX(1, test, " ");
//									PrintStringX(1, test, "IDS_UI_INFO_SUPPORT_SHOOTING");
//									PrintStringLiteralX(1, test, "\n");
//								}

//							POA += increment;
//						}
//					else
//						{
//							if (charging == 0)
//								{
//									// Reduce effect vs armoured or better foot, or heavily armoured cavalry
//									if (((IsFoot(enemy) == 1) && (GetAttrib(enemy, "BodyArmour") >= 100)) || ((IsMounted(enemy) == 1) && (GetAttrib(enemy, "BodyArmour") >= 300)))
//										{
//											increment *= 2;
//											increment /= 3;
//										}

//									POA += increment;
//									if ((print == 1) && (increment > 0))
//										{
//											if (any_printed == 1)
//												{
//													PrintStringLiteralX(1, test, ", ");
//												}
//											PrintStringX(1, test, "IDS_SUPPORT_SHOOTING");
//											PrintStringLiteralX(1, test, " +");
//											PrintIntX(1, test, increment);
//											any_printed = 1;
//										}
//								}
//						}
//				}
//		}

	// Heavy Lancers
	percent = GetAttrib(me, "Heavy_Lancers");
	if (percent >= 50)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 14);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_HEAVY_LANCERS");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 75; // average vs horse and foot
				}
			else
				{
					increment = 100;

					// Cancelled if charging steady foot who have pikes, offensive or defensive spearmen capability or are in protective terrain.
					if (charging == 1)
						{
							if ((GetAttrib(enemy, "Pike") > 0) || (GetAttrib(enemy, "Offensive_Spearmen") > 0) || (GetAttrib(enemy, "Defensive_Spearmen") > 0) || (InProtectiveTerrain(enemy, me, enemy_charging) == 1))
								{
									increment = PercentNotSteady(enemy, me, enemy_charging); // Only get POA against non-steady - which may be partial
								}
						}

					// Cancelled vs elephants and Battle Wagons
//          if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Battle_Wagons") == 1))
					if (IsUnitSquadType(enemy, "Elephants") == 1)
						{
							increment = 0;
						}

					// Cancelled if not in open
					if (IsCombatInOpen(me, enemy) == 0)
						{
							increment = 0;
						}
				}

			increment *= percent;
			increment /= 100;

			POA += increment;
			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 14);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}

	// Light Lancers
	percent = GetAttrib(me, "Light_Lancers");
	if (percent >= 50)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 15);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_LIGHT_LANCERS");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 70; // average vs horse and foot. Slightly reduced because likely to meet heavy lancers.
				}
			else
				{
					increment = 100;

					// Cancelled if charging steady foot who have pikes, offensive or defensive spearmen capability or are in protective terrain.
					if (charging == 1)
						{
							if ((GetAttrib(enemy, "Pike") > 0) || (GetAttrib(enemy, "Offensive_Spearmen") > 0) || (GetAttrib(enemy, "Defensive_Spearmen") > 0) || (InProtectiveTerrain(enemy, me, enemy_charging) == 1))
								{
									increment = PercentNotSteady(enemy, me, enemy_charging); // Only get POA against non-steady - which may be partial
								}
						}

					// Cancelled vs heavy Lancers
					if (GetAttrib(enemy, "Heavy_Lancers") >= 50)
						{
							increment = 0;
					}

					// Cancelled vs elephants and Battle Wagons
//          if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Battle_Wagons") == 1))
					if (IsUnitSquadType(enemy, "Elephants") == 1)
						{
							increment = 0;
						}

					// Cancelled if not in open
					if (IsCombatInOpen(me, enemy) == 0)
						{
							increment = 0;
						}
				}

			increment *= percent;
			increment /= 100;

			POA += increment;

			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 15);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}

	// Elephants
	if (IsUnitSquadType(me, "Elephants") == 1)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 19);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_ELEPHANTS");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 250;  // average vs horse and foot

					POA += increment;
				}
			else
				{
					increment = 250;
					POA += increment;
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 19);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}

	// Heavy Chariots
	if (IsUnitSquadType(me, "Heavy_Chariots") == 1)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 14);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_HEAVY_CHARIOTS");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 100;  // average vs horse and foot
					POA += increment;
				}
			else
				{
					increment = 100;

					// Cancelled if charging steady foot who have pikes, offensive or defensive spearmen capability or are in protective terrain.
					if (charging == 1)
						{
							if ((GetAttrib(enemy, "Pike") > 0) || (GetAttrib(enemy, "Offensive_Spearmen") > 0) || (GetAttrib(enemy, "Defensive_Spearmen") > 0) || (InProtectiveTerrain(enemy, me, enemy_charging) == 1))
								{
									increment = PercentNotSteady(enemy, me, enemy_charging); // Only get POA against non-steady - which may be partial
								}
						}

					// Cancelled vs Lancers
					if ((GetAttrib(enemy, "Heavy_Lancers") >= 50) || (GetAttrib(enemy, "Light_Lancers") >= 50))
						{
							increment = 0;
						}

					// Cancelled vs elephants, Battle Wagons, light foot or light horse.
//          if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Battle_Wagons") == 1) || (IsUnitSquadType(enemy, "Light_Foot") == 1) || (IsUnitSquadType(enemy, "Light_Horse") == 1))
					if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Light_Foot") == 1) || (IsUnitSquadType(enemy, "Light_Horse") == 1))
						{
							increment = 0;
						}

					// Cancelled if not in open
					if (IsCombatInOpen(me, enemy) == 0)
						{
							increment = 0;
						}

					POA += increment;
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 14);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}

	// Scythed Chariots
	if (IsUnitSquadType(me, "Scythed_Chariots") == 1)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 17);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_SCYTHED_CHARIOTS");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 250;  // average vs horse and foot
					POA += increment;
				}
			else
				{
					increment = 250;

					// Cancelled if charging steady foot who have pikes, offensive or defensive spearmen capability or are in protective terrain.
					if (charging == 1)
						{
							if ((GetAttrib(enemy, "Pike") > 0) || (GetAttrib(enemy, "Offensive_Spearmen") > 0) || (GetAttrib(enemy, "Defensive_Spearmen") > 0) || (InProtectiveTerrain(enemy, me, enemy_charging) == 1))
								{
									increment = PercentNotSteady(enemy, me, enemy_charging); // Only get POA against non-steady - which may be partial
								}
						}

					// Cancelled vs Lancers
					if ((GetAttrib(enemy, "Heavy_Lancers") >= 50) || (GetAttrib(enemy, "Light_Lancers") >= 50))
						{
							increment = 0;
						}

					// Cancelled vs elephants, Battle Wagons, light foot or light horse.
//          if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Battle_Wagons") == 1) || (IsUnitSquadType(enemy, "Light_Foot") == 1) || (IsUnitSquadType(enemy, "Light_Horse") == 1))
					if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Light_Foot") == 1) || (IsUnitSquadType(enemy, "Light_Horse") == 1))
						{
							increment = 0;
						}

					// Cancelled if not in open
					if (IsCombatInOpen(me, enemy) == 0)
						{
							increment = 0;
						}

					POA += increment;
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 17);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}


	// Mounted other than Scythed Chariots (and Elephants - which don't count as mounted) vs light foot, bowmen or mob in open.
	if (enemy == -1)
		{
			if (print == 1)
				{
					if ((IsMounted(me) == 1) && (IsUnitSquadType(me, "Scythed_Chariots") == 0)) // This assumes that Elephants do NOT count as mounted.
						{
							any_printed = 1;
							PrintStringX(1, test, "IDS_TT_MOUNTED_TROOPS");
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_MOUNTED_TROOPS");
							PrintStringLiteralX(1, test, "\n");
						}
				}
		}
	else
		{
			increment = 0;
			if ((IsMounted(me) == 1) && (IsUnitSquadType(me, "Scythed_Chariots") == 0) && (IsCombatInOpen(me, enemy) == 1)) // This assumes that Elephants do NOT count as mounted.
				{
					if ((IsUnitSquadType(enemy, "Bowmen") == 1) || (IsUnitSquadType(enemy, "Light_Foot") == 1) || (IsUnitSquadType(enemy, "Mob") == 1))
						{
							increment = 100;
						}
				}

			POA += increment;

			if ((print == 1) && (increment > 0))
				{
					if (any_printed == 1)
						{
							PrintStringLiteralX(1, test, ", ");
						}
					PrintStringX(1, test, "IDS_TT_MOUNTED_V_SOFT_FOOT");
					PrintStringLiteralX(1, test, " ");
					PrintTroopTypesStringX(1, test, enemy);
					PrintStringLiteralX(1, test, " +");
					PrintIntX(1, test, increment);
					any_printed = 1;
				}
		}

	// Uphill bonus.
	// Need at least two versions.
	// 1) Gentle slope: Use for positions where slope gives uphill troops slight advantage, but not sufficient to deter bold non-stupid enemy from attacking and winning if their troops are better.
	// 2) Steep slope: Use for positions that would normally be considered "too strong to attack".
	if (enemy != -1)
		{
			increment = 0;
			height_difference = GetTileHeight(GetUnitX(me), GetUnitY(me)) - GetTileHeight(GetUnitX(enemy), GetUnitY(enemy));
			if ((height_difference > 0) && (height_difference < 76)) // The slope from flat ground to a level 1 hill, or from a level 1 hill to a level 2 hill etc.
				{
					increment = 25; // This may need tweaking.
				}
			if (height_difference >= 76) // The slope from flat ground to a level 2 or 3 hill.
				{
					increment = 100; // May also need tweaking.
				}

			POA += increment;
			if ((print == 1) && (increment > 0))
				{
					if (any_printed == 1)
						{
							PrintStringLiteralX(1, test, ", ");
						}
					PrintStringX(1, test, "IDS_TT_UPHILL");
					PrintStringLiteralX(1, test, " +");
					PrintIntX(1, test, increment);
					any_printed = 1;
				}
		}

	// Obstacle/Fortifications bonus (not if mounted troops or battle wagons and not if attacking)
	if (enemy != -1)
		{
//      if ((IsMounted(me) == 0) && (IsUnitSquadType(me, "Battle_Wagons") == 0) && (charging == 0))
			if ((IsMounted(me) == 0) && (charging == 0))
				{
					increment = 0;
					obstacleType = IsTileEdgeDefendibleObstacle(GetUnitX(me), GetUnitY(me), GetUnitX(enemy), GetUnitY(enemy));

					if ((obstacleType == 0) || (obstacleType == 1)) // Ditch or Hedge/Low Wall
						{
							increment = Max(0, 25 - ProtectionIncrementAlreadyReceived);
						}

					if (obstacleType > 1)
						{
							if (obstacleType == 2) // Fortifications
								{
									if (GetTerrainCoverValue(GetUnitX(me), GetUnitY(me), 3) == 3) // heavy forts
										{
											increment = 200;
										}
									else
										{
											increment = 100;
										}
								}
						}

					if (obstacleType > -1)
						{
							POA += increment;
							if ((print == 1) && (increment > 0))
								{
									if (any_printed == 1)
										{
											PrintStringLiteralX(1, test, ", ");
										}
									if (obstacleType > 1)
										{
											PrintStringX(1, test, "IDS_TT_DEFENDING_FORTIFICATIONS");
										}
									else
										{
											PrintStringX(1, test, "IDS_TT_DEFENDING_OBSTACLE");
										}
									PrintStringLiteralX(1, test, " +");
									PrintIntX(1, test, increment);
									any_printed = 1;
								}
						}
				}
		}

	// Adjust for quality
	increment = GetQuality(me) - 100; // Standard quality is 100. Only adjust POA for quality above or below 100.
	increment /= 2;  // 200 quality (+100 above average) is worth +50 POA
	POA += increment
	if ((print == 1) && (increment != 0))
		{

			if ((enemy != -1) && (any_printed == 1))
				{
					PrintStringLiteralX(1, test, ", ");
				}

			PrintStringX(1, test, "IDS_TT_QUALITY");

			if (enemy == -1)
				{
					PrintStringLiteralX(1, test, ":");
				}

			PrintStringLiteralX(1, test, " ");

			if (increment > 0)
				{
					PrintStringLiteralX(1, test, "+");
				}

			PrintIntX(1, test, increment);

			if (enemy == -1)
				{
					PrintStringLiteralX(1, test, " ");
					PrintStringX(1, test, "IDS_UI_INFO_QUALITY");
					PrintStringLiteralX(1, test, "\n");
				}

			any_printed = 1;
		}

	// Adjust for general
	if (GetAttrib(me, "General") > -1)
		{
			increment = 50
			POA += increment
			if ((print == 1) && (increment != 0))
				{

					if ((enemy != -1) && (any_printed == 1))
						{
							PrintStringLiteralX(1, test, ", ");
						}

					PrintStringX(1, test, "IDS_TT_GENERAL");

					if (enemy == -1)
						{
							PrintStringLiteralX(1, test, ":");
						}

					PrintStringLiteralX(1, test, " ");

					if (increment > 0)
						{
							PrintStringLiteralX(1, test, "+");
						}

					PrintIntX(1, test, increment);

					if (enemy == -1)
						{
							PrintStringLiteralX(1, test, " ");
							PrintStringX(1, test, "IDS_UI_INFO_QUALITY");
							PrintStringLiteralX(1, test, "\n");
						}

					any_printed = 1;
				}
		}
		
//	// Stratagem POAs
//	gAnyPrintedFlag = 0;
//	increment = StratagemPOAs(me, enemy, charging, print, test, any_printed, 0)
//	POA += increment;
//	if ((print == 1) && (gAnyPrintedFlag == 1))
//		{
//			any_printed = 1;
//		}

	// Custom POA
	increment = TryToCallScenarioFunction("CustomImpactPOA", me, enemy, charging, print, test, any_printed);
	POA += increment;
	if ((print == 1) && (increment != 0))
		{
			any_printed = 1;
		}

	// If no capabilities
	if ((print == 1) && (any_printed == 0))
		{
			PrintStringX(1, test, "IDS_TT_NO_IMPACT_CAPABILITIES");
			if (enemy == -1)
				{
					PrintStringLiteralX(1, test, ".\n");
				}
			else
				{
					PrintStringLiteralX(1, test, " ");
					PrintStringX(1, test, "IDS_TT_APPLY");
				}
		}

	return POA;
}

// Returns Melee POA (* 100) against the current enemy.
// If enemy == -1 then this give a general purpose POA for calculating general purpose close combat rating - which does not take into account cohesion state or disorder
// attacking indicates whether "me" is attacking - needed for terrain disorder and defending obstacles to work correctly
FUNCTION GetMeleePOA(me, enemy, attacking, print, test)
{
	int percent;
	int POA;
	int percent_12ranks;
	int percent_16ranks;
	int percent_disordered;
	int percent_severely_disordered;
	int increment;
	int percent_ignoring_armour;
	int armour_difference;
	int height_difference;
	int enemy_attacking;
	int obstacleType;
	int myArmour;
	int enemyArmour;
	int percentNonShot;
	int myPercentIncluded;
	int percent_not_ignoring_armour;
	int any_printed;
	int multiplier;
	int difference;
	int overlappers;
	int strength;
	int ProtectionIncrementAlreadyReceived;

	any_printed = 0;
	ProtectionIncrementAlreadyReceived = 0;

	enemy_attacking = attacking + 1;
	enemy_attacking = enemy_attacking % 2;

	POA = 0;

	percent_disordered = Min(PercentDisordered(me, enemy, attacking), 100);
	percent_severely_disordered = Max(PercentDisordered(me, enemy, attacking) - 100, 0);

	// Pike
	percent = GetAttrib(me,"Pike");
	if (percent > 0)
		{
			percent_12ranks = Percent12RanksPike(me);
			percent_16ranks = Percent16RanksPike(me);

			if (percent_12ranks > 0)
				{
					if (enemy == -1)
						{
							if (print == 1)
								{
									any_printed = 1;
									PrintStringIndexedX(1, test, "IDS_CAPABILITY", 18);
									PrintStringLiteralX(1, test, ": +");
									PrintIntX(1, test, percent_12ranks);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_UI_INFO_MELEE_PIKE");
									PrintStringLiteralX(1, test, "\n");
								}

							if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
								{
									increment = 100 - percent_severely_disordered;  // average vs horse and foot
									increment *= percent_12ranks; // Basic POA only for pikes over 12 ranks deep (equivalent to 3 FOGAM ranks).
									increment /= 100;
									POA += increment;
								}
						}
					else
						{
							if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
								{
									increment = 100 - percent_severely_disordered;
									increment *= percent_12ranks; // Basic POA only for pikes over 12 ranks deep (equivalent to 3 FOGAM ranks).
									increment /= 100;
									POA += increment;
									if ((print == 1) && (increment > 0))
										{
											if (any_printed == 1)
												{
													PrintStringLiteralX(1, test, ", ");
												}
											PrintStringIndexedX(1, test, "IDS_CAPABILITY", 18);
											PrintStringLiteralX(1, test, " +");
											PrintIntX(1, test, increment);
											any_printed = 1;
										}
								}
						}

					if (percent_16ranks > 0)
						{
							if (enemy == -1)
								{
									if (print == 1)
										{
											any_printed = 1;
											PrintStringX(1, test, "IDS_UNIT_DEEP_PIKE");
											PrintStringLiteralX(1, test, ": +");
											PrintIntX(1, test, percent_16ranks);
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_UI_INFO_MELEE_DEEP_PIKE");
											PrintStringLiteralX(1, test, "\n");
										}

									// If unit has large block of pike/heavy weapon/swordsmen and is not disrupted or disordered
									if (GetAttrib(me,"MoraleState") < 1)
										{
											if (percent_disordered == 0)
												{
													increment = percent_16ranks;
													POA += increment;
												}
										}
								}
							else
								{
									if (GetAttrib(me,"MoraleState") < 1) // If not DISRUPTED
										{
											// If unit has large block of pike/heavy weapon/swordsmen, and not disordered
											if (percent_disordered == 0)
												{
													increment = percent_16ranks;
													POA += increment;
													if ((print == 1) && (increment > 0))
														{
															if (any_printed == 1)
																{
																	PrintStringLiteralX(1, test, ", ");
																}
															PrintStringX(1, test, "IDS_UNIT_DEEP_PIKE");
															PrintStringLiteralX(1, test, " +");
															PrintIntX(1, test, increment);
															any_printed = 1;
														}
												}
										}
								}
						}
				}
		}

	// Protected Pikeless Foot Shooters. NOTE ** Will also need to exclude any other cases where Protection has already been taken into account. **
	// Also include artillery.
	if (enemy != -1)
		{
			if (IsFoot(me) == 1)
				{
					percent = PercentShooters(me);
					if (IsArtillery(me) == 1)
						{
							percent = 100;
						}

					if ((percent > 0) && (GetAttrib(me, "Pike") == 0))
						{
							if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
								{
									// If enemy is mounted and we are in terrain that confers protection
									if (IsMounted(enemy) == 1)
										{
											if (InProtectiveTerrain(me, enemy, attacking) == 1)
												{
													increment = 100 - percent_severely_disordered;
													ProtectionIncrementAlreadyReceived = increment;
													POA += increment;
													if ((print == 1) && (increment > 0))
														{
															if (any_printed == 1)
																{
																	PrintStringLiteralX(1, test, ", ");
																}
															PrintStringX(1, test, "IDS_TT_PROTECTED");
															PrintStringLiteralX(1, test, " +");
															PrintIntX(1, test, increment);
															any_printed = 1;
														}
												}
										}
								}
						}
				}
		}

	// Artillery in the open
	if (IsArtillery(me) == 1)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringX(1, test, "IDS_UI_INFO_ARTILLERY");
						}
					
					increment = -100; // Average of Protected and Unprotected
					POA += increment;
				}
			else
				{
					if (InProtectiveTerrain(me, enemy, attacking) != 1)
						{
							increment = -200;
							POA += increment;
							if (print == 1)
								{
									if (any_printed == 1)
										{
											PrintStringLiteralX(1, test, ", ");
										}
									PrintStringX(1, test, "IDS_TT_UNPROTECTED");
									PrintStringLiteralX(1, test, " ");
									PrintIntX(1, test, increment);
									any_printed = 1;
								}
						}
				}
		}

	// Heavy Weapon
	percent = GetAttrib(me, "Heavy_Weapon");
	if (percent > 0)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 10);
							PrintStringLiteralX(1, test, ": ");
							if (Percent16RanksPike(me) == 0)
								{
									if ((GetAttrib(me, "Pike") == 0) || (GetAttrib(me,"MoraleState") >= 2))
										{
											PrintStringLiteralX(1, test, "+");
											PrintIntX(1, test, percent);
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_UI_INFO_MELEE_HEAVY_WEAPON_MOUNTED");
											PrintStringLiteralX(1, test, " ");
										}
									PrintStringLiteralX(1, test, "+");
									PrintIntX(1, test, percent);
									PrintStringLiteralX(1, test, " ");
									PrintStringX(1, test, "IDS_UI_INFO_MELEE_HEAVY_WEAPON_FOOT");
									PrintStringLiteralX(1, test, " ");
								}

//							PrintStringX(1, test, "IDS_UI_INFO_MELEE_OVERLAP");
							PrintStringX(1, test, "IDS_UI_INFO_IGNORE_ARMOUR");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 100; // average vs horse and foot
				}
			else
				{
					increment = 100;

					// Cancelled if already counting POA for pike in unit.
					if (Percent12RanksPike(me) > 0)
						{
							if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
								{
									increment = 0;
								}
						}
				}

			increment *= percent;
			increment /= 100;

			POA += increment;
			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 10);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}

		}

	// Offensive Spearmen
	percent = GetAttrib(me, "Offensive_Spearmen");
	if (percent > 0)
		{
			// Take account of non-spearmen - usually archers - being in rear ranks beyond the unitsize 600 depth, so not reducing the spear POA.
			if (percent < 100)
				{
					strength = GetAttrib(me, "TotalMen") * 100 ;
					strength /= StartingStrength(me);
					strength *= GetAttrib(me, "UnitSize");
					strength /= 100;
					if (strength > 600)
						{
							percent *= strength;
							percent /= 600;
							percent = Min(percent, 100);
						}
				}

			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 24);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_SPEARMEN_PREAMBLE"); // Can use same string as for Impact
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, percent);
							PrintStringLiteralX(1, test, " ");
							PrintStringX(1, test, "IDS_UI_INFO_MELEE_SPEARMEN");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 0;

					if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
						{
							increment = 100; // average vs horse and foot
							multiplier = Max(0, percent - percent_severely_disordered);
							increment *= multiplier;
							increment /= 100;
						}
				}
			else
				{
					increment = 0;

					// Only get POA if not FRAGMENTED or Severely Disordered
					if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
						{
							increment = 100;
							multiplier = Max(0, percent - percent_severely_disordered);
							increment *= multiplier;
							increment /= 100;
						}
				}

			POA += increment;
			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 24);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}

		}

	// Defensive Spearmen
	percent = GetAttrib(me, "Defensive_Spearmen");
	if (percent > 0)
		{
			// Take account of non-spearmen - usually archers - being in rear ranks beyond the unitsize 600 depth, so not reducing the spear POA.
			if (percent < 100)
				{
					strength = GetAttrib(me, "TotalMen") * 100 ;
					strength /= StartingStrength(me);
					strength *= GetAttrib(me, "UnitSize");
					strength /= 100;
					if (strength > 600)
						{
							percent *= strength;
							percent /= 600;
							percent = Min(percent, 100);
						}
				}

			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 22);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_IMPACT_SPEARMEN_PREAMBLE"); // Can use same string as for Impact
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, percent);
							PrintStringLiteralX(1, test, " ");
							PrintStringX(1, test, "IDS_UI_INFO_MELEE_SPEARMEN");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 0;

					if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
						{
							increment = 100; // average vs horse and foot
							multiplier = Max(0, percent - percent_severely_disordered);
							increment *= multiplier;
							increment /= 100;
						}
				}
			else
				{
					increment = 0;

					// Only get POA if not FRAGMENTED or Severely Disordered
					if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
						{
							increment = 100;
							multiplier = Max(0, percent - percent_severely_disordered);
							increment *= multiplier;
							increment /= 100;
						}
				}

			POA += increment;
			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 22);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}

		}

	// Swordsmen
	percent = GetAttrib(me, "Swordsmen");
	if (percent > 0)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 25);
							PrintStringLiteralX(1, test, ": ");
							if (Percent16RanksPike(me) == 0)
								{
									if ((GetAttrib(me, "Pike") == 0) || (GetAttrib(me,"MoraleState") >= 2))
										{
											PrintStringLiteralX(1, test, "+");
											PrintIntX(1, test, percent);
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_UI_INFO_MELEE_SWORDSMEN_MOUNTED");
											PrintStringLiteralX(1, test, " ");
										}
									PrintStringLiteralX(1, test, "+");
									PrintIntX(1, test, percent);
									PrintStringLiteralX(1, test, " ");
									if (IsFoot(me) == 1)
										{
											PrintStringX(1, test, "IDS_UI_INFO_MELEE_FOOT_SWORDSMEN_FOOT");
										}
									else
										{
											PrintStringX(1, test, "IDS_UI_INFO_MELEE_MOUNTED_SWORDSMEN_FOOT");
										}
									PrintStringLiteralX(1, test, " ");
									if (IsFoot(me) == 1)
										{
											PrintStringLiteralX(1, test, "+");
											PrintIntX(1, test, percent/2);
											PrintStringLiteralX(1, test, " ");
											PrintStringX(1, test, "IDS_UI_INFO_MELEE_FOOT_SWORDSMEN_FOOT2");
											PrintStringLiteralX(1, test, " ");
										}
								}
//							if (IsFoot(me) == 1)
//								{
//									PrintStringX(1, test, "IDS_UI_INFO_MELEE_OVERLAP");
//								}

							PrintStringLiteralX(1, test, "\n");
						}

					if (IsMounted(me) == 1)
						{
							increment = 80; // average vs horse and foot
						}
					else
						{
							increment = 90; // average vs horse and foot
						}
				}
			else
				{
					increment = 100;

					if (IsMounted(me) == 1)
						{
							// Mounted swordsmen POA cancelled vs steady foot who have PIKE, offensive spearmen or defensive spearmen capability or are in protective terrain.
							if ((GetAttrib(enemy, "Pike") > 0) || (GetAttrib(enemy, "Offensive_Spearmen") > 0) || (GetAttrib(enemy, "Defensive_Spearmen") > 0) || (InProtectiveTerrain(enemy, me, enemy_attacking) == 1))
								{
									increment = PercentNotSteady(enemy, me, enemy_attacking); // Only get POA against non-steady - which may be partial
								}
						}
					else
						{
							// Foot swordsmen POA mitigated to 50 vs steady foot who have offensive spearmen or defensive spearmen capability or are in protective terrain.
							if ((GetAttrib(enemy, "Offensive_Spearmen") > 0) || (GetAttrib(enemy, "Defensive_Spearmen") > 0) || (InProtectiveTerrain(enemy, me, enemy_attacking) == 1))
								{
									increment = PercentNotSteady(enemy, me, enemy_attacking) / 2;
									increment += 50;
								}
						}

					// Cancelled vs elephants
					if (IsUnitSquadType(enemy, "Elephants") == 1)
						{
							increment = 0;
						}


					// Cancelled if already counting POA for pike in unit.
					if (Percent12RanksPike(me) > 0)
						{
							if (GetAttrib(me,"MoraleState") < 2) // If not FRAGMENTED
								{
									increment = 0;
								}
						}
				}

			increment *= percent;
			increment /= 100;

			POA += increment;
			if (enemy != -1)
				{
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_CAPABILITY", 25);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}

	// Elephants
	if (IsUnitSquadType(me, "Elephants") == 1)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 19);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_MELEE_ELEPHANTS");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 100;  // average vs horse and foot
					POA += increment;
				}
			else
				{
					increment = 100;
					POA += increment;
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 19);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}

	// Heavy Chariots
	if (IsUnitSquadType(me, "Heavy_Chariots") == 1)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 14);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_MELEE_HEAVY_CHARIOTS");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 100;  // average vs horse and foot
					POA += increment;
				}
			else
				{
					increment = 100;

					// Cancelled vs steady foot who have pikes, offensive spearmen or defensive spearmen capability or are in protective terrain.
					if ((GetAttrib(enemy, "Pike") > 0) || (GetAttrib(enemy, "Offensive_Spearmen") > 0) || (GetAttrib(enemy, "Defensive_Spearmen") > 0) || (InProtectiveTerrain(enemy, me, enemy_attacking) == 1))
						{
							increment = PercentNotSteady(enemy, me, enemy_attacking); // Only get POA against non-steady - which may be partial}
						}

					// Cancelled vs elephants, Battle Wagons, light foot or light horse.
//          if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Battle_Wagons") == 1) || (IsUnitSquadType(enemy, "Light_Foot") == 1) || (IsUnitSquadType(enemy, "Light_Horse") == 1))
					if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Light_Foot") == 1) || (IsUnitSquadType(enemy, "Light_Horse") == 1))
						{
							increment = 0;
						}


					POA += increment;
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 14);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}

	// Scythed Chariots
	if (IsUnitSquadType(me, "Scythed_Chariots") == 1)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 17);
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_MELEE_SCYTHED_CHARIOTS");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = 100;  // average vs horse and foot
					POA += increment;
				}
			else
				{
					increment = 100;

					// Cancelled vs steady foot who have pikes, offensive spearmen or defensive spearmen capability or are in protective terrain.
					if ((GetAttrib(enemy, "Pike") > 0) || (GetAttrib(enemy, "Offensive_Spearmen") > 0) || (GetAttrib(enemy, "Defensive_Spearmen") > 0) || (InProtectiveTerrain(enemy, me, enemy_attacking) == 1))
						{
							increment = PercentNotSteady(enemy, me, enemy_attacking); // Only get POA against non-steady - which may be partial}
						}

					// Cancelled vs elephants, Battle Wagons, light foot or light horse.
//          if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Battle_Wagons") == 1) || (IsUnitSquadType(enemy, "Light_Foot") == 1) || (IsUnitSquadType(enemy, "Light_Horse") == 1))
					if ((IsUnitSquadType(enemy, "Elephants") == 1) || (IsUnitSquadType(enemy, "Light_Foot") == 1) || (IsUnitSquadType(enemy, "Light_Horse") == 1))
						{
							increment = 0;
						}


					POA += increment;
					if ((print == 1) && (increment > 0))
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringIndexedX(1, test, "IDS_SQUADTYPE", 17);
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}

	// Armour advantage
	if (enemy == -1)
		{
			if (print == 1)
				{
					increment = GetAttrib(me,"BodyArmour") / 2;

					if (increment > 0)
						{
							increment = Min(50, increment);
							any_printed = 1;
							PrintStringX(1, test, "IDS_TT_ARMOUR_ADVANTAGE");
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_TT_UPTO");
							PrintStringLiteralX(1, test, " +");
							PrintIntX(1, test, increment);
							PrintStringLiteralX(1, test, " ");
							PrintStringX(1, test, "IDS_UI_INFO_MELEE_ARMOUR");
							PrintStringLiteralX(1, test, "\n");
						}
				}

			increment = GetAttrib(me,"BodyArmour") * 15; // Arbitrary multiplier, might need tweaking.
			increment /= 100;
		}
	else
		{
			increment = 0;
			myArmour = GetAttrib(me, "BodyArmour");
			enemyArmour = GetAttrib(enemy, "BodyArmour");

			armour_difference = myArmour - enemyArmour;
			armour_difference = Min(100, armour_difference);
			if (armour_difference > 0)
				{
					// No armour advantage vs elephants, chariots, battle wagons or artillery
//          if ((IsUnitSquadType(enemy, "Elephants") == 0) && (IsUnitSquadType(enemy, "Light_Chariots") == 0) && (IsUnitSquadType(enemy, "Heavy_Chariots") == 0) && (IsUnitSquadType(enemy, "Scythed_Chariots") == 0) && (IsUnitSquadType(enemy, "Battle_Wagons") == 0) && (IsArtillery(enemy) == 0))
					if ((IsUnitSquadType(enemy, "Elephants") == 0) && (IsUnitSquadType(enemy, "Light_Chariots") == 0) && (IsUnitSquadType(enemy, "Heavy_Chariots") == 0) && (IsUnitSquadType(enemy, "Scythed_Chariots") == 0) && (IsArtillery(enemy) == 0))
						{
							// Heavy weapon cancels armour advantage
							percent_ignoring_armour = GetAttrib(enemy,"Heavy_Weapon");

							percent_not_ignoring_armour = 100 - percent_ignoring_armour;

							increment = percent_not_ignoring_armour;
							increment *= 50;  // Maximum armour advantage = 50 (i.e. half a full POA. This makes armour advantage less effective than in FOG, where it is probably over-effective.)
							increment *= armour_difference;
							increment /= 10000;
						}
				}
		}
	POA += increment;
	if ((enemy != -1) && (print == 1) && (increment > 0))
		{
			if (any_printed == 1)
				{
					PrintStringLiteralX(1, test, ", ");
				}
			PrintStringX(1, test, "IDS_TT_ARMOUR_ADVANTAGE");
			PrintStringLiteralX(1, test, " +");
			PrintIntX(1, test, increment);
			any_printed = 1;
		}

	// Poor Stamina malus
	if (HasPoorStamina(me) == 1)
		{
			if (enemy == -1)
				{
					if (print == 1)
						{
							any_printed = 1;
							PrintStringX(1, test, "IDS_POOR_STAMINA");
							PrintStringLiteralX(1, test, ": ");
							PrintStringX(1, test, "IDS_UI_INFO_MELEE_POOR_STAMINA");
							PrintStringLiteralX(1, test, "\n");
						}

					increment = -25;  // average vs horse and foot
					POA += increment;
				}
			else
				{
					increment = -25;
					POA += increment;
					if (print == 1)
						{
							if (any_printed == 1)
								{
									PrintStringLiteralX(1, test, ", ");
								}
							PrintStringX(1, test, "IDS_POOR_STAMINA");
							PrintIntX(1, test, increment);
							any_printed = 1;
						}
				}
		}

	// Uphill bonus.
	// Need at least two versions.
	// 1) Gentle slope: Use for positions where slope gives uphill troops slight advantage, but not sufficient to deter bold non-stupid enemy from attacking and winning if their troops are better.
	// 2) Steep slope: Use for positions that would normally be considered "too strong to attack".
	if (enemy != -1)
		{
			increment = 0;
			height_difference = GetTileHeight(GetUnitX(me), GetUnitY(me)) - GetTileHeight(GetUnitX(enemy), GetUnitY(enemy));
			if ((height_difference > 0) && (height_difference < 76)) // The slope from flat ground to a level 1 hill, or from a level 1 hill to a level 2 hill etc.
				{
					increment = 25; // This may need tweaking.
				}
			if (height_difference >= 76) // The slope from flat ground to a level 2 or 3 hill.
				{
					increment = 100; // May also need tweaking.
				}

			POA += increment;
			if ((print == 1) && (increment > 0))
				{
					if (any_printed == 1)
						{
							PrintStringLiteralX(1, test, ", ");
						}
					PrintStringX(1, test, "IDS_TT_UPHILL");
					PrintStringLiteralX(1, test, " +");
					PrintIntX(1, test, increment);
					any_printed = 1;
				}
		}

	// Fortifications bonus (not if mounted troops or battle wagons and not if attacking)
	if (enemy != -1)
		{
//      if ((IsMounted(me) == 0) && (IsUnitSquadType(me, "Battle_Wagons") == 0) && (attacking == 0))
			if ((IsMounted(me) == 0) && (attacking == 0))
				{
					increment = 0;
					obstacleType = IsTileEdgeDefendibleObstacle(GetUnitX(me), GetUnitY(me), GetUnitX(enemy), GetUnitY(enemy));

					if ((obstacleType == 0) || (obstacleType == 1))
						{
							increment = Max(0, 25 - ProtectionIncrementAlreadyReceived);
						}

					if (obstacleType > 1)
						{
							if (obstacleType == 2) // Fortifications
								{
									if (GetTerrainCoverValue(GetUnitX(me), GetUnitY(me), 3) == 3) // heavy forts
										{
											increment = 200;
										}
									else
										{
											increment = 100;
										}
								}
						}

					if (obstacleType > -1)
						{
							POA += increment;
							if ((print == 1) && (increment > 0))
								{
									if (any_printed == 1)
										{
											PrintStringLiteralX(1, test, ", ");
										}
									if (obstacleType > 1)
										{
											PrintStringX(1, test, "IDS_TT_DEFENDING_FORTIFICATIONS");
										}
									else
										{
											PrintStringX(1, test, "IDS_TT_DEFENDING_OBSTACLE");
										}
									PrintStringLiteralX(1, test, " +");
									PrintIntX(1, test, increment);
									any_printed = 1;
								}
						}
				}
		}

	// Adjust for quality
	increment = GetQuality(me) - 100; // Standard quality is 100. Only adjust POA for quality above or below 100.
	increment /= 2;  // 200 quality (+100 above average) is worth +50 POA
	POA += increment
	if ((print == 1) && (increment != 0))
		{
			if ((enemy != -1) && (any_printed == 1))
				{
					PrintStringLiteralX(1, test, ", ");
				}

			PrintStringX(1, test, "IDS_TT_QUALITY");

			if (enemy == -1)
				{
					PrintStringLiteralX(1, test, ":");
				}

			PrintStringLiteralX(1, test, " ");

			if (increment > 0)
				{
					PrintStringLiteralX(1, test, "+");
				}

			PrintIntX(1, test, increment);

			if (enemy == -1)
				{
					PrintStringLiteralX(1, test, " ");
					PrintStringX(1, test, "IDS_UI_INFO_QUALITY");
					PrintStringLiteralX(1, test, "\n");
				}

			any_printed = 1;
		}

	// Adjust for general
	if (GetAttrib(me, "General") > -1)
		{
			increment = 50
			POA += increment
			if ((print == 1) && (increment != 0))
				{

					if ((enemy != -1) && (any_printed == 1))
						{
							PrintStringLiteralX(1, test, ", ");
						}

					PrintStringX(1, test, "IDS_TT_GENERAL");

					if (enemy == -1)
						{
							PrintStringLiteralX(1, test, ":");
						}

					PrintStringLiteralX(1, test, " ");

					if (increment > 0)
						{
							PrintStringLiteralX(1, test, "+");
						}

					PrintIntX(1, test, increment);

					if (enemy == -1)
						{
							PrintStringLiteralX(1, test, " ");
							PrintStringX(1, test, "IDS_UI_INFO_QUALITY");
							PrintStringLiteralX(1, test, "\n");
						}

					any_printed = 1;
				}
		}

//	// Stratagem POAs
//	gAnyPrintedFlag = 0;
//	increment = StratagemPOAs(me, enemy, attacking, print, test, any_printed, 1)
//	POA += increment;
//	if ((print == 1) && (gAnyPrintedFlag == 1))
//		{
//			any_printed = 1;
//		}

	// Custom POA
	increment = TryToCallScenarioFunction("CustomMeleePOA", me, enemy, attacking, print, test, any_printed);
	POA += increment;
	if ((print == 1) && (increment != 0))
		{
			any_printed = 1;
		}

	// If no capabilities
	if ((print == 1) && (any_printed == 0))
		{
			PrintStringX(1, test, "IDS_TT_NO_MELEE_CAPABILITIES");
			if (enemy == -1)
				{
					PrintStringLiteralX(1, test, ".\n");
				}
			else
				{
					PrintStringLiteralX(1, test, " ");
					PrintStringX(1, test, "IDS_TT_APPLY");
				}
		}

	return POA;
}

// Returns the number of men lost
FUNCTION GetLosses(me, damage)
{
	int men_lost;
	int unitSize;

	unitSize = GetAttrib(me, "UnitSize");

	// Men_lost is calculated from original strength of unit, as incoming fire/close combat damage is not reduced by unit's losses,
	// and the divisor is based on the unitSize of the unit - with a minimum unitSize of 300 (or double actual unitSize if lower) to stop small units being too vulnerable.
	men_lost = StartingStrength(me) * damage;
	men_lost /= Max(unitSize, Min(300, unitSize * 2));

	return men_lost;
}

FUNCTION GetLossFactor(me, damage)
{
	int lossFactor;
	int unitSize;

	unitSize = GetAttrib(me, "UnitSize");

	// lossFactor is calculated from original strength of unit, as incoming fire/close combat damage is not reduced by unit's losses,
	// and the divisor is based on the unitSize of the unit - with a minimum unitSize of 300 (or double actual unitSize if lower) to stop small units being too vulnerable.
	lossFactor = StartingStrength(me) * damage * 100;
	lossFactor /= Max(unitSize, Min(300, unitSize * 2));
	lossFactor *= 100;
	lossFactor /= GetAttrib(me, "TotalMen");
	

	return lossFactor;
}

// Prints shooting weapon modifier - helper function for shooting tooltip
FUNCTION PrintShootingWeaponModifier(test, POA)
	{
		int modifier;

		modifier = POA * 33;
		modifier /= 100;

		if (modifier > 0)
			{
				PrintStringLiteralX(1, test, "+");
			}

		PrintIntX(1, test, modifier);
		PrintStringLiteralX(1, test, "%");
	}

// Prints number of effective shots - helper function for shooting tooltip
FUNCTION PrintEffectiveShots(test, me, percent, volley, equivalent)
{
	int shots;
	int maximum_effective_unit_size;

	if (IsMounted(me) == 1)
		{
			maximum_effective_unit_size = 400;
		}
	else
		{
			maximum_effective_unit_size = 600;
		}

	shots = percent * Min(GetAttrib(me, "UnitSize"), maximum_effective_unit_size);
	shots /= 100;
	shots *= 400; // To stop the number of effective shots from a 200 man 400 UnitSize horse unit exceeding the number of men
								// (no reason why they shouldn't but it is less intuitive).
	shots /= 600;
	shots *= GetAttrib(me, "TotalMen");
	shots /= StartingStrength(me);
	if (volley < 3)
		{
			shots /= 2;
		}
	shots *= MountedShootingModifier(me);
	shots /= 100;
	shots *= GetStrengthMultiplier();
	shots/= 100;
	
	// Cosmetic overall reduction in reported shots
	shots *= 240;
	shots /= 400;
	
	// Reduce reported effective shots for low ammunition
	if (IsAmmunitionLow(me) == 1)
		{
			shots /= 2;
		}

	PrintStringLiteralX(1, test, ": ");

	if (equivalent == 1)
		{
			PrintStringX(1, test,"IDS_TT_EQUIVALENT");
			PrintStringLiteralX(1, test, " ");
		}
	PrintIntX(1, test, shots * 2); // Double the number of shots to account for shooting twice
	PrintStringLiteralX(1, test, " ");
	PrintStringX(1, test, "IDS_TT_EFFECTIVE");
	PrintStringLiteralX(1, test, " ");
	if (equivalent == 1)
		{
			PrintStringX(1, test,"IDS_TT_SMALLARMS");
			PrintStringLiteralX(1, test, " ");
		}
	PrintStringX(1, test, "IDS_TT_SHOTS");
}

// Print win/draw/lose string for assault tooltip
// index is the index for the WinDrawLose Universal Variable arrays - needed to stop string being recalculated constantly when been done for multiple opponents of unit in melee
// if forceRecalc == 1, forces recalculate
FUNCTION CalculateWinDrawLose(me, enemy, phase, meEffectiveSize, enemyEffectiveSize, meDamageInflicted, enemyDamageInflicted, index, forceRecalc)
{
	int i;
	int wins;
	int draws;
	int losses;
	int losing_difference_constant;
	int meMin;
	int meMax;
	int enemyMin;
	int enemyMax;
	int difference;
	int average_size;
	int meDamageCaused;
	int enemyDamageCaused;
	int iterations;
	int divisor;
	int adjustment;

	iterations = 1000;

	// Log("meX, meY, RealX, RealY", GetUniversalArray("meX", index), GetUniversalArray("meY", index), GetUniversalArray("RealX", index), GetUniversalArray("RealY", index));
	// Log("enemyX, enemyY, enemy X, enemy Y", GetUniversalArray("enemyX", index), GetUniversalArray("enemyY", index), GetUnitX(enemy), GetUnitY(enemy));

	if ((forceRecalc == 1) || (GetUniversalArray("meX", index) != GetUniversalArray("RealX", index)) || (GetUniversalArray("meY", index) != GetUniversalArray("RealY", index)) || (GetUniversalArray("enemyX", index) != GetUnitX(enemy)) || (GetUniversalArray("enemyY", index) != GetUnitY(enemy)))
		{
//			Log("Recalculating combat odds: unit, enemy", me, enemy);
			losing_difference_constant = 16;

			wins = 0;
			draws = 0;
			losses = 0;

			meMin = meDamageInflicted / 2;
			meMax = meDamageInflicted * 3;
			meMax /= 2;

			enemyMin = enemyDamageInflicted / 2;
			enemyMax = enemyDamageInflicted * 3;
			enemyMax /= 2;

			for (i = 0; i < iterations; i++)
			{
				// These randomised lines MUST use the same randomizing algorithm as SkewedRandom(min, max, 1) but with FXRand() substituting for Rand()
				meDamageCaused = FXRand(meMin, meMax) + FXRand(meMin, meMax);
				meDamageCaused /= 2;

				enemyDamageCaused = FXRand(enemyMin, enemyMax) + FXRand(enemyMin, enemyMax);
				enemyDamageCaused /= 2;

				difference = enemyDamageCaused - meDamageCaused;
				// Adjust difference so that smaller combats are not less decisive
				average_size = meEffectiveSize + enemyEffectiveSize;
				average_size /= 2;
				difference *= 600;
				difference /= average_size;

				if (difference > losing_difference_constant) // losing_difference value specified above (same as in ResolvCloseCombat())
					{
						// Enemy won combat
						losses += 1;
					}
				else
					{
						if (difference < -losing_difference_constant)
							{
								// Me won combat
								wins += 1;
							}
						else // (difference from -16 to +16: Nobody counts as losing the combat
							{
								draws += 1;
							}
					}
			}

			divisor = iterations / 100;
			adjustment = divisor / 2;
			wins += adjustment;
			wins /= divisor;
			draws += adjustment;
			draws /= divisor;
			losses += adjustment;
			losses /= divisor;

			if (phase == 0)
				{
					SetUniversalArray("ImpactWin", index, wins);
					SetUniversalArray("ImpactDraw", index, draws);
					SetUniversalArray("ImpactLoss", index, losses);
				}
			else
				{
					SetUniversalArray("MeleeWin", index, wins);
					SetUniversalArray("MeleeDraw", index, draws);
					SetUniversalArray("MeleeLoss", index, losses);
				}
		}

	if (forceRecalc == 1) // Make sure Win/Draw/Loss will be recalculated next time this function is accessed after forced recalc (actual combat). Also force other tooltips to be recalculated after any combat.
		{
      ResetWinDrawLoseFlags();
      ResetTooltipFlags();
		}
	else
		{
			if (phase == 1)
				{
					SetUniversalArray("meX", index, GetUniversalArray("RealX", index));
					SetUniversalArray("meY", index, GetUniversalArray("RealY", index));
					SetUniversalArray("enemyX", index, GetUnitX(enemy));
					SetUniversalArray("enemyY", index, GetUnitY(enemy));
				}
		}
}

// Print win/draw/lose string for assault tooltip
// index is the index for the WinDrawLose Campaign Variable arrays - needed to stop string being recalculated constantly when been done for multiple opponents of unit in melee
// If linefeed = 1 print a linefeed before the win/draw/lose string.
FUNCTION PrintWinDrawLoseString(phase, index, linefeed)
{
	if (linefeed == 1)
		{
			PrintStringLiteralX(1, 1, "\n");
		}

	PrintStringX(1, 1, "IDS_TT_WIN");
	PrintStringLiteralX(1, 1, " ");

	if (phase == 0)
		{
			PrintIntX(1, 1, GetUniversalArray("ImpactWin", index));
		}
	else
		{
			PrintIntX(1, 1, GetUniversalArray("MeleeWin", index));
		}

	PrintStringLiteralX(1, 1, "%, ");
	PrintStringX(1, 1, "IDS_TT_DRAW");
	PrintStringLiteralX(1, 1, " ");

	if (phase == 0)
		{
			PrintIntX(1, 1, GetUniversalArray("ImpactDraw", index));
		}
	else
		{
			PrintIntX(1, 1, GetUniversalArray("MeleeDraw", index));
		}

	PrintStringLiteralX(1, 1, "%, ");
	PrintStringX(1, 1, "IDS_TT_LOSE");
	PrintStringLiteralX(1, 1, " ");

	if (phase == 0)
		{
			PrintIntX(1, 1, GetUniversalArray("ImpactLoss", index));
		}
	else
		{
			PrintIntX(1, 1, GetUniversalArray("MeleeLoss", index));
		}

	PrintStringLiteralX(1, 1, "%");
}

// This function is only used to trigger shooting tooltip "Long Range" message and is therefore designed for speed, does not determine if target is actually out of range.
// Returns 1 if target is outside close range of any type in the unit that has a long and short range. Otherwise 0.
FUNCTION IsLongRange(me, target)
{
	int ret;
	int distance;

	ret = 0;
	distance = GetDistanceBetween(me, target);

	if ((GetAttrib(me, "Bow") > 0) || (GetAttrib(me, "Crossbow") > 0))
		{
			if (IsFoot(me) == 1)
				{
					if (distance > 2)
						{
							ret = 1;
						}
				}
		}

	if (GetAttrib(me, "Heavy_Artillery") > 0)
		{
			if (distance > 6)
				{
					ret = 1;
				}
		}

	return ret;
}

// Gets close combat rating to display - emphasises differences by cubing internal close combat rating
FUNCTION GetDisplayCloseCombatRating(me)
{
	int ret;
	int rating;

	rating = CalculateModifiedCloseCombatRating(me, -1, -1, 0);

	ret = rating * rating * rating;
	ret /= 10000;

	return ret;
}

// Helper function for Close Combat reporting - calculates "Advantage/Disadvantage" string from Win/Draw/Loss ratings.
FUNCTION PrintAdvantage(stringIndex, me, enemy, phase, combatIndex)
{
	int win;
	int lose;
	int temp;

	if (phase == 0)
		{
			win = Max(1, GetUniversalArray("ImpactWin", combatIndex));
			lose = Max(1, GetUniversalArray("ImpactLoss", combatIndex));
		}
	else
		{
			win = Max(1, GetUniversalArray("MeleeWin", combatIndex));
			lose = Max(1, GetUniversalArray("MeleeLoss", combatIndex));
		}

	Log("Unit1, Unit2, win, lose", me, enemy, win, lose);

	temp = win + lose;
	if (temp < 10)
		{
			PrintStringX(stringIndex, 0, "IDS_EQUAL_TERMS");
		}
	else
		{
			temp = lose * 75;
			if (win >= temp)
				{
					PrintStringX(stringIndex, 0, "IDS_ADVANTAGE_MASSIVE");
				}
			else
				{
					temp = lose * 45;
					if (win >= temp)
						{
							PrintStringX(stringIndex, 0, "IDS_ADVANTAGE_GREAT");
						}
					else
						{
							temp = lose * 20;
							if (win >= temp)
								{
									PrintStringX(stringIndex, 0, "IDS_ADVANTAGE");
								}
							else
								{
									temp = lose * 3;
									temp /= 2;
									if (win >= temp)
										{
											PrintStringX(stringIndex, 0, "IDS_ADVANTAGE_SLIGHT");
										}
									else
										{
											temp = win * 3;
											temp /= 2;
											if (lose < temp)
												{
													PrintStringX(stringIndex, 0, "IDS_EQUAL_TERMS");
												}
											else
												{
													temp = win * 20;
													if (lose < temp)
														{
															PrintStringX(stringIndex, 0, "IDS_DISADVANTAGE_SLIGHT");
														}
													else
														{
															temp = win * 45;
															if (lose < temp)
																{
																	PrintStringX(stringIndex, 0, "IDS_DISADVANTAGE");
																}
															else
																{
																	temp = win * 75;
																	if (lose < temp)
																		{
																			PrintStringX(stringIndex, 0, "IDS_DISADVANTAGE_GREAT");
																		}
																	else
																		{
																			PrintStringX(stringIndex, 0, "IDS_DISADVANTAGE_MASSIVE");
																		}
																}
														}
												}
										}
								}
						}
				}
		}
}

// Modifier to reduce close range shooting by non-light foot (to roughly equivalent to max 1.5 FOGAM ranks). Intended to cope (approximately) with units including mixed shooters.
FUNCTION CloseRangeNonLightFootShootingModifier(me)
{
	int modifier;

	modifier = 100;

	if ((IsFoot(me) == 1) && (IsLightTroops(me) == 0))
		{
			modifier = 75 * 100;
			modifier /= PercentShooters(me);
			modifier = Min(modifier, 100);
		}

	return modifier;
}

// Clears both units Melee Due flag against each other
FUNCTION ClearMeleeDueFlags(me, enemy)
{
	int i;
	int id;

	for (i = 0; i < 8; i++)
	{
		if (GetAttribArray(me,"Enemy",i) == enemy)
			{
				SetAttribArray(me,"MeleeDue",i, 0);
				i = 8;
			}
	}

	for (i = 0; i < 8; i++)
	{
		if (GetAttribArray(enemy,"Enemy",i) == me)
			{
				SetAttribArray(enemy,"MeleeDue",i, 0);
				i = 8;
			}
	}
}

// Returns 1 if Melee is due between these two units
FUNCTION IsMeleeDue(me, enemy)
{
	int i;
	int id;
	int ret;

	ret = 0;

	for (i = 0; i < 8; i++)
	{
		if (GetAttribArray(me,"Enemy",i) == enemy)
			{
				if ((GetAttrib(me, "MoraleState") < 3) && (GetAttrib(enemy, "MoraleState") < 3)) // Return 0 if either unit already routed. ("MeleeDue" attrib will not be reset until pursuit ceases)
					{
						if (GetAttribArray(me,"MeleeDue",i) == 1)
							{
								ret = 1;
								i = 8;
							}
					}
			}
	}

	return ret;
}

// Returns 1 if any melee is still to be fought by this unit, otherwise 0
FUNCTION IsAnyMeleeDue(me)
{
	int i;
	int id;
	int ret;
	int enemy;

	ret = 0;
	
	if (GetAttrib(me, "MoraleState") < 3)
		{
			for (i = 0; i < 8; i++)
			{
				enemy = GetAttribArray(me,"Enemy",i) 
				if (enemy != -1)
					{
						if ((GetAttrib(enemy, "MoraleState") < 3)) // Return 0 if either unit already routed. ("MeleeDue" attrib will not be reset until pursuit ceases)
							{
								if (GetAttribArray(me,"MeleeDue",i) == 1)
									{
										ret = 1;
										i = 8;
									}
							}
					}
			}
		}

	return ret;
}

// Returns 0 if ammunition is not low, 1 if it is
FUNCTION IsAmmunitionLow(me)
{
	int ret;
	
	ret = 0;
	
	if (gIgnoreAmmunition[GetUnitSide(me)] == 0)
		{
			if (GetAttrib(me, "TotalShots") >= FULL_EFFECT_SHOTS)
				{
					ret = 1;
				}
		}
		
	return ret;
}

// Returns 1 if unit is baggage, otherwise 0
FUNCTION IsBaggage(id)
{
	int ret;
	
	ret = 0;
	
	GetUnitTypeString(GetUnitTypeIndex(id));	
	if (StringCompareLeft(GetWorkString(),"Baggage",7) == 1)
		{
			ret = 1;
		}
	
	return ret;
}

// Helper function for unit list and next unshot
FUNCTION HasAnyTarget(me, max_range)
{
	int total;
	int i;
	int id;
	int max;
	int distance;
	int has_target;
	int x;
	int y;
	int side;
	
	side = GetUnitSide(me);
	
	has_target = 0;

	total = MakeClosestUnitsToUnitList(me,1);

	for (i = 0; i < total; i++)
		{
			id = GetClosestUnit(i);

			if (id != -1)
				{
					x = GetUnitX(id);
					y = GetUnitY(id);
					
					if (IsValidTile(x, y) == 1)
						{
							distance = GetDistanceBetween(me, id);

							if (distance > max_range)
								{
									i = total;
								}
							else
								{
									if (GetTileLOS(x, y, side) == 1) // Though not as accurate, this is used instead of GetUnitLOSToUnit(me, id) to reduce processing overheads.
										{
											if (CallUnitFunctionDirect(me, "CHECK_Unit_Shoot", me, id, x, y, 0, 1) == 0)
												{
													has_target = 1;
													i = total;
												}
										}
								}
						}
				}
		}

	return has_target;
}

// Returns 1 if unit has poor stamina, otherwise 0. 
// Currently only applies to warbands.
FUNCTION HasPoorStamina(me)
{
	int ret;
	
	if ((IsUnitSquadType(me, "Warriors") == 1) || (IsUnitSquadType(me, "Undrilled_Heavy_Foot") == 1))
		{
			if (GetBaseAttrib(me, "Impact_Foot") >= 33)
				{
					ret = 1;
				}
		}
		
	ret = 0; // **** Function currently disabled ****
		
	return ret;
}

FUNCTION UnitTypeHasPoorStamina(typeIndex)
{
	int ret;
	
	if ((IsUnitTypeSquadType(typeIndex, "Warriors") == 1) || (IsUnitTypeSquadType(typeindex, "Undrilled_Heavy_Foot") == 1))
		{
			if (GetBaseAttrib(typeIndex + 65536, "Impact_Foot") >= 33)
				{
					ret = 1;
				}
		}
		
	ret = 0; // **** Function currently disabled ****
		
	return ret;	
}

// Returns 1 if unit qualifies as warband, 0 if not
FUNCTION IsWarband(me)
{
	int ret;
	
	if ((IsUnitSquadType(me, "Warriors") == 1) || (IsUnitSquadType(me, "Undrilled_Heavy_Foot") == 1))
		{
			if (GetBaseAttrib(me, "Impact_Foot") >= 33)
				{
					ret = 1;
				}
		}
		
	return ret;
}

